Skip to content

Commit 0b4b2de

Browse files
Added sqrt decomposition (TheAlgorithms#613)
* Create binary_heap.go * Update binary_heap.go * fix * Update binary_heap.go * Create sqrt_decomposition_simple.go * move files * bono * bono * fix style * fix style * . * some changes * . * .
1 parent a3beede commit 0b4b2de

File tree

2 files changed

+182
-0
lines changed

2 files changed

+182
-0
lines changed

sqrt/sqrtdecomposition.go

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Package sqrt contains algorithms and data structures that contains a √n in their complexity
2+
package sqrt
3+
4+
import "math"
5+
6+
// Sqrt (or Square Root) Decomposition is a technique used for query an array and perform updates
7+
// Inside this package is described its most simple data structure, you can find more at: https://cp-algorithms.com/data_structures/sqrt_decomposition.html
8+
//
9+
// Formally, You can use SqrtDecomposition only if:
10+
//
11+
// Given a function $Query:E_1,...,E_n\rightarrow Q$
12+
//
13+
// if $\exist unionQ:Q,Q\rightarrow Q$
14+
//
15+
// s.t.
16+
//
17+
// - $\forall n\in \N > 1, 1\le i<n, E_1,..., E_n\in E \\ query(E_1,..., E_n)=unionQ(query(E_1,..., E_i), query(E_{i+1},...,E_n))$
18+
//
19+
// - (Only if you want use $update$ function)
20+
// $\forall n\in \N > 0, E_1,..., E_n\in E \\ query(E_1,...,E_{new},..., E_n)=updateQ(query(E_1,...,E_{old},...,E_n), indexof(E_{old}), E_{new})$
21+
type SqrtDecomposition[E any, Q any] struct {
22+
querySingleElement func(element E) Q
23+
unionQ func(q1 Q, q2 Q) Q
24+
updateQ func(oldQ Q, oldE E, newE E) (newQ Q)
25+
26+
elements []E
27+
blocks []Q
28+
blockSize uint64
29+
}
30+
31+
// Create a new SqrtDecomposition instance with the parameters as specified by SqrtDecomposition comment
32+
// Assumptions:
33+
// - len(elements) > 0
34+
func NewSqrtDecomposition[E any, Q any](
35+
elements []E,
36+
querySingleElement func(element E) Q,
37+
unionQ func(q1 Q, q2 Q) Q,
38+
updateQ func(oldQ Q, oldE E, newE E) (newQ Q),
39+
) *SqrtDecomposition[E, Q] {
40+
sqrtDec := &SqrtDecomposition[E, Q]{
41+
querySingleElement: querySingleElement,
42+
unionQ: unionQ,
43+
updateQ: updateQ,
44+
elements: elements,
45+
}
46+
sqrt := math.Sqrt(float64(len(sqrtDec.elements)))
47+
blockSize := uint64(sqrt)
48+
numBlocks := uint64(math.Ceil(float64(len(elements)) / float64(blockSize)))
49+
sqrtDec.blocks = make([]Q, numBlocks)
50+
for i := uint64(0); i < uint64(len(elements)); i++ {
51+
if i%blockSize == 0 {
52+
sqrtDec.blocks[i/blockSize] = sqrtDec.querySingleElement(elements[i])
53+
} else {
54+
sqrtDec.blocks[i/blockSize] = sqrtDec.unionQ(sqrtDec.blocks[i/blockSize], sqrtDec.querySingleElement(elements[i]))
55+
}
56+
}
57+
sqrtDec.blockSize = blockSize
58+
return sqrtDec
59+
}
60+
61+
// Performs a query from index start to index end (non included)
62+
// Assumptions:
63+
// - start < end
64+
// - start and end are valid
65+
func (s *SqrtDecomposition[E, Q]) Query(start uint64, end uint64) Q {
66+
firstIndexNextBlock := ((start / s.blockSize) + 1) * s.blockSize
67+
q := s.querySingleElement(s.elements[start])
68+
if firstIndexNextBlock > end { // if in same block
69+
start++
70+
for start < end {
71+
q = s.unionQ(q, s.querySingleElement(s.elements[start]))
72+
start++
73+
}
74+
} else {
75+
// left side
76+
start++
77+
for start < firstIndexNextBlock {
78+
q = s.unionQ(q, s.querySingleElement(s.elements[start]))
79+
start++
80+
}
81+
82+
//middle part
83+
endBlock := end / s.blockSize
84+
for i := firstIndexNextBlock / s.blockSize; i < endBlock; i++ {
85+
q = s.unionQ(q, s.blocks[i])
86+
}
87+
88+
// right part
89+
for i := endBlock * s.blockSize; i < end; i++ {
90+
q = s.unionQ(q, s.querySingleElement(s.elements[i]))
91+
}
92+
}
93+
return q
94+
}
95+
96+
// Assumptions:
97+
// - index is valid
98+
func (s *SqrtDecomposition[E, Q]) Update(index uint64, newElement E) {
99+
i := index / s.blockSize
100+
s.blocks[i] = s.updateQ(s.blocks[i], s.elements[index], newElement)
101+
s.elements[index] = newElement
102+
}

sqrt/sqrtdecomposition_test.go

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package sqrt_test
2+
3+
import (
4+
"github.com/TheAlgorithms/Go/sqrt"
5+
"testing"
6+
)
7+
8+
// Query interval
9+
type query struct {
10+
firstIndex uint64
11+
lastIndex uint64
12+
}
13+
14+
type update struct {
15+
index uint64
16+
value int
17+
}
18+
19+
func TestSqrtDecomposition(t *testing.T) {
20+
var sqrtDecompositionTestData = []struct {
21+
description string
22+
array []int
23+
updates []update
24+
queries []query
25+
expected []int
26+
}{
27+
{
28+
description: "test 1-sized array",
29+
array: []int{1},
30+
queries: []query{{0, 1}},
31+
expected: []int{1},
32+
},
33+
{
34+
description: "test array with size 5",
35+
array: []int{1, 2, 3, 4, 5},
36+
queries: []query{{0, 5}, {0, 2}, {2, 4}},
37+
expected: []int{15, 3, 7},
38+
},
39+
{
40+
description: "test array with size 5 and updates",
41+
array: []int{1, 2, 3, 4, 5},
42+
updates: []update{{index: 1, value: 3},
43+
{index: 2, value: 4}},
44+
queries: []query{{0, 5}, {0, 2}, {2, 4}},
45+
expected: []int{17, 4, 8},
46+
},
47+
{
48+
description: "test array with size 11 and updates",
49+
array: []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
50+
updates: []update{{index: 2, value: 2},
51+
{index: 3, value: 3},
52+
{index: 6, value: 6}},
53+
queries: []query{{3, 5}, {7, 8}, {3, 7}, {0, 10}},
54+
expected: []int{4, 1, 11, 18},
55+
},
56+
}
57+
for _, test := range sqrtDecompositionTestData {
58+
t.Run(test.description, func(t *testing.T) {
59+
s := sqrt.NewSqrtDecomposition(test.array,
60+
func(e int) int { return e },
61+
func(q1, q2 int) int { return q1 + q2 },
62+
func(q, a, b int) int { return q - a + b },
63+
)
64+
65+
for i := 0; i < len(test.updates); i++ {
66+
s.Update(test.updates[i].index, test.updates[i].value)
67+
}
68+
69+
for i := 0; i < len(test.queries); i++ {
70+
result := s.Query(test.queries[i].firstIndex, test.queries[i].lastIndex)
71+
72+
if result != test.expected[i] {
73+
t.Logf("FAIL: %s", test.description)
74+
t.Fatalf("Expected result: %d\nFound: %d\n", test.expected[i], result)
75+
}
76+
}
77+
78+
})
79+
}
80+
}

0 commit comments

Comments
 (0)