What is the computational complexity of this loop in the Go programming language?
var a []int
for i := 0 ; i < n ; i++ {
a = append(a, i)
}
Does append operate in linear time (reallocating memory and copying everything on each append), or in amortized constant time (like the way vector classes in many languages are implemnted)?
The Go Programming Language Specification says that the append built-in function reallocates if necessary.
Appending to and copying slices
If the capacity of s is not large enough to fit the additional values,
append allocates a new, sufficiently large slice that fits both the
existing slice elements and the additional values. Thus, the returned
slice may refer to a different underlying array.
The precise algorithm to grow the target slice, when necessary, for an append is implementation dependent. For the current gc compiler algorithm, see the growslice function in the Go runtime package slice.go source file. It's amortized constant time.
In part, the amount-to-grow slice computation reads:
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.len < 1024 {
newcap = doublecap
} else {
for newcap < cap {
newcap += newcap / 4
}
}
}
ADDENDUM
The Go Programming Language Specification allows implementors of the language to implement the append built-in function in a number of ways.
For example, new allocations only have to be "sufficiently large". The amount allocated may be parsimonius, allocating the minimum necessary amount, or generous, allocating more than the minimum necessary amount to minimize the cost of resizing many times. The Go gc compiler uses a generous dynamic array amortized constant time algorithm.
The following code illustrates two legal implementations of the append built-in function. The generous constant function implements the same amortized constant time algorithm as the Go gc compiler. The parsimonius variable function, once the initial allocation is filled, reallocates and copies everything every time. The Go append function and the Go gccgo compiler are used as controls.
package main
import "fmt"
// Generous reallocation
func constant(s []int, x ...int) []int {
if len(s)+len(x) > cap(s) {
newcap := len(s) + len(x)
m := cap(s)
if m+m < newcap {
m = newcap
} else {
for {
if len(s) < 1024 {
m += m
} else {
m += m / 4
}
if !(m < newcap) {
break
}
}
}
tmp := make([]int, len(s), m)
copy(tmp, s)
s = tmp
}
if len(s)+len(x) > cap(s) {
panic("unreachable")
}
return append(s, x...)
}
// Parsimonious reallocation
func variable(s []int, x ...int) []int {
if len(s)+len(x) > cap(s) {
tmp := make([]int, len(s), len(s)+len(x))
copy(tmp, s)
s = tmp
}
if len(s)+len(x) > cap(s) {
panic("unreachable")
}
return append(s, x...)
}
func main() {
s := []int{0, 1, 2}
x := []int{3, 4}
fmt.Println("data ", len(s), cap(s), s, len(x), cap(x), x)
a, c, v := s, s, s
for i := 0; i < 4096; i++ {
a = append(a, x...)
c = constant(c, x...)
v = variable(v, x...)
}
fmt.Println("append ", len(a), cap(a), len(x))
fmt.Println("constant", len(c), cap(c), len(x))
fmt.Println("variable", len(v), cap(v), len(x))
}
Output:
gc:
data 3 3 [0 1 2] 2 2 [3 4]
append 8195 9152 2
constant 8195 9152 2
variable 8195 8195 2
gccgo:
data 3 3 [0 1 2] 2 2 [3 4]
append 8195 9152 2
constant 8195 9152 2
variable 8195 8195 2
To summarize, depending on the implementation, once the initial capacity is filled, the append built-in function may or may not reallocate on every call.
References:
Dynamic array
Amortized analysis
Appending to and copying slices
If the capacity of s is not large enough to fit the additional values,
append allocates a new, sufficiently large slice that fits both the
existing slice elements and the additional values. Thus, the returned
slice may refer to a different underlying array.
Append to a slice specification discussion
The spec (at tip and 1.0.3) states:
"If the capacity of s is not large enough to fit the additional
values, append allocates a new, sufficiently large slice that fits
both the existing slice elements and the additional values. Thus, the
returned slice may refer to a different underlying array."
Should this be an "If and only if"? For example, if I know the
capacity of my slice is sufficiently long, am I assured that I will
not change the underlying array?
Rob Pike
Yes you are so assured.
runtime slice.go source file
Arrays, slices (and strings): The mechanics of 'append'
It doesn't reallocate on every append and it is quite explicitly stated in the docs:
If the capacity of s is not large enough to fit the additional values, append allocates a new, sufficiently large slice that fits both the existing slice elements and the additional values. Thus, the returned slice may refer to a different underlying array.
Amortized constant time is thus the complexity asked about.
Related
I have written a function and I can't seem to find where the bug is:
The function change works like this:
An input of 15 (target value) with possible values of [1, 5, 10, 25, 100] should return [5, 10]. That's because to reach a target value of 15, the least amount of numbers to make up that target number is to have a 10 and 5
I use a caching mechanism, as it is a recursive function and remembers the values that have already been calculated.
func Change(coins []int, target int, resultsCache map[int][]int) ([]int, error) {
if val, ok := resultsCache[target]; ok {
return val, nil
}
if target == 0 {
return make([]int, 0), nil
}
if target < 0 {
return nil, errors.New("Target can't be less than zero")
}
var leastNumOfCoinChangeCombinations []int
for _, coin := range coins {
remainder := target - coin
remainderCombination, _ := Change(coins, remainder, resultsCache)
if remainderCombination != nil {
combination := append(remainderCombination, coin)
if leastNumOfCoinChangeCombinations == nil || len(combination) < len(leastNumOfCoinChangeCombinations) {
leastNumOfCoinChangeCombinations = combination
}
}
}
if leastNumOfCoinChangeCombinations == nil {
return nil, errors.New("Can't find changes from coin combinations")
}
sort.Ints(leastNumOfCoinChangeCombinations)
resultsCache[target] = leastNumOfCoinChangeCombinations
return leastNumOfCoinChangeCombinations, nil
}
The cache however have some abnormal behaviour, for example if I want to use the value of 12 in the cache later, instead of getting [2,5,5], I get [1 2 5] instead. Not sure where I went wrong. (but initially it was calculated and stored correctly, not sure how it got changed).
Here is a playground I used for troubleshooting:
https://play.golang.org/p/Rt8Sh_Ul-ge
You are encountering a fairly common, but sometimes difficult to spot, issue caused by the way slices work. Before reading further it's probably worth scanning the blog post Go Slices: usage and internals. The issue stems from the way append can reuse the slices underlying array as per this quote from the spec:
If the capacity of s is not large enough to fit the additional values, append allocates a new, sufficiently large underlying array that fits both the existing slice elements and the additional values. Otherwise, append re-uses the underlying array.
The below code provides a simple demonstration of what is occurring:
package main
import (
"fmt"
"sort"
)
func main() {
x := []int{2, 3}
x2 := append(x, 4)
x3 := append(x2, 1)
fmt.Println("x2 before sort", x2)
sort.Ints(x3)
fmt.Println("x2 after sort", x2)
fmt.Println("x3", x3)
fmt.Println("x2 cap", cap(x2))
}
The results are (playground):
x2 before sort [2 3 4]
x2 after sort [1 2 3]
x3 [1 2 3 4]
x2 cap 4
The result is probably not what you expected - why did x2 change when we sorted x3? The reason this happens is that the backing array for x2 has a capacity of 4 (length is 3) and when we append 1 the new slice x3 uses the same backing array (capacity 4, length 4). This only becomes an issue when we make a change to the portion of the backing array used by x2 and this happens when we call sort on x3.
So in your code you are adding a slice to the map but it's backing array is then being altered after that instance of Change returns (the append/sort ends up happening pretty much as in the example above).
There are a few ways you can fix this; removing the sort will do the trick but is probably not what you want. A better alternative is to take a copy of the slice; you can do this by replacing combination := append(remainderCombination, coin) with:
combination := make([]int, len(remainderCombination)+1)
copy(combination , remainderCombination)
combination[len(remainderCombination)] = coin
or the simpler (but perhaps not as easy to grasp - playground):
combination := append([]int{coin}, remainderCombination...)
I am using the go combinations package to find all the combinations of a list of ints. The package out of the box is for strings but I've edited to do []int instead.
func All(set []int) (subsets [][]int) {
length := uint(len(set))
// Go through all possible combinations of objects
// from 1 (only first object in subset) to 2^length (all objects in subset)
for subsetBits := 1; subsetBits < (1 << length); subsetBits++ {
var subset []int
for object := uint(0); object < length; object++ {
// checks if object is contained in subset
// by checking if bit 'object' is set in subsetBits
if (subsetBits>>object)&1 == 1 {
// add object to subset
subset = append(subset, set[object])
}
}
// add subset to subsets
subsets = append(subsets, subset)
}
return subsets
}
This works for me, however, only when given a small slice. once the slice gets large the combinations become exponential and tolling to calculate.
Luckily I know when I need to stop. Before running the combinations I have determined the max length of the combinations.
s := []int{2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4}
limit := 2
The above would generate an exorbent amount of combinations, majority I will not need, as I can define the limit to the length of combinations I'd need as a maximum.
// Ideal output
[[2] [2] [2] [2] ...... [2 3] [2 4]] // Stop once we hit length of 3 as limit was set to 2
I can't really figure out how to implement a limit break or return once the combinations reach a certain length.
Example:
func All(set []int, limit int) (subsets [][]int) {
// ... previous code here
if len(combinations) > limit {
return output
}
}
I'm dealing with a programming problem
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
and with input n = 5, k = 4, the output should be [[1,2,3,4],[1,2,3,5],[1,2,4,5],[1,3,4,5],[2,3,4,5]], the following is my golang solution
func combine(n int, k int) [][]int {
result := [][]int{}
comb := []int{}
subcom(0, k, n, &comb, &result)
return result
}
func subcom(s, k, n int, comb *[]int, result *[][]int) {
if k > 0 {
for i := s + 1; i <= n-k+1; i++ {
c := append(*comb, i)
subcom(i, k-1, n, &c, result)
}
} else {
*result = append(*result, *comb)
}
}
I think my solution is right, but it return [[1 2 3 5] [1 2 3 5] [1 2 4 5] [1 3 4 5] [2 3 4 5]].
After debugging, I found [1 2 3 4] was added to the result slice at the beginning, but later changed to [1 2 3 5], resulting in the repetition of two [1 2 3 5]s. But I can't figure out what's wrong here.
This is a common mistake when using append.
When your code runs c:=append(*comb,i), it tries to first use the allocated memory in the underlying array to add a new item and only create a new slice when it failed to do so. This is what changes the [1 2 3 4] to [1 2 3 5] - because they share the same underlying memory.
To fix this, copy when you want to append into result:
now := make([]int,len(*comb))
copy(now,*comb)
*result = append(*result,now)
Or use a shortcut of copying:
*result = append(*result, append([]int{},*comb...))
Update:
To understand what I mean by underlying memory, one should understandd the internal model of Go's slice.
In Go, a slice has a data structure called SliceHeader which is accessible through reflect package and is what being referred to when you use unsafe.Sizeof and taking address.
The SliceHeader taking cares of three elements: Len,Cap and a Ptr. The fisrt two is trivail: they are what len() and cap() is for. The last one is a uintptr that points to the memory of the data the slice is containing.
When you shallow-copy a slice, a new SliceHeader is created but with the same content, including Ptr. So the underlying memory is not copied, but shared.
What is the difference between cap and len of a slice in golang?
According to definition:
A slice has both a length and a capacity.
The length of a slice is the number of elements it contains.
The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice.
x := make([]int, 0, 5) // len(b)=0, cap(b)=5
Does the len mean non null values only?
A slice is an abstraction that uses an array under the covers.
cap tells you the capacity of the underlying array. len tells you how many items are in the array.
The slice abstraction in Go is very nice since it will resize the underlying array for you, plus in Go arrays cannot be resized so slices are almost always used instead.
Example:
s := make([]int, 0, 3)
for i := 0; i < 5; i++ {
s = append(s, i)
fmt.Printf("cap %v, len %v, %p\n", cap(s), len(s), s)
}
Will output something like this:
cap 3, len 1, 0x1040e130
cap 3, len 2, 0x1040e130
cap 3, len 3, 0x1040e130
cap 6, len 4, 0x10432220
cap 6, len 5, 0x10432220
As you can see once the capacity is met, append will return a new slice with a larger capacity. On the 4th iteration you will notice a larger capacity and a new pointer address.
Play example
I realize you did not ask about arrays and append but they are pretty foundational in understanding the slice and the reason for the builtins.
From the source code:
// The len built-in function returns the length of v, according to its type:
// Array: the number of elements in v.
// Pointer to array: the number of elements in *v (even if v is nil).
// Slice, or map: the number of elements in v; if v is nil, len(v) is zero.
// String: the number of bytes in v.
// Channel: the number of elements queued (unread) in the channel buffer;
// if v is nil, len(v) is zero.
func len(v Type) int
// The cap built-in function returns the capacity of v, according to its type:
// Array: the number of elements in v (same as len(v)).
// Pointer to array: the number of elements in *v (same as len(v)).
// Slice: the maximum length the slice can reach when resliced;
// if v is nil, cap(v) is zero.
// Channel: the channel buffer capacity, in units of elements;
// if v is nil, cap(v) is zero.
func cap(v Type) int
Simple explanation
Slice are self growing form of array so there are two main properties.
Length is total no of elements() the slice is having and can be used for looping through the elements we stored in slice. Also when we print the slice all elements till length gets printed.
Capacity is total no elements in underlying array, when you append more elements the length increases till capacity. After that any further append to slice causes the capacity to increase automatically(apprx double) and length by no of elements appended.
The real magic happens when you slice out sub slices from a slice where all the actual read/write happens on the underlaying array. So any change in sub slice will also change data both in original slice and underlying array. Where as any sub slices can have their own length and capacity.
Go through the below program carefully. Its modified version of golang tour example
package main
import "fmt"
func main() {
sorig := []int{2, 3, 5, 7, 11, 13}
printSlice(sorig)
// Slice the slice to give it zero length.
s := sorig[:0]
printSlice(s)
// Extend its length.
s = s[:4]
s[2] = 555
printSlice(s)
// Drop its first two values.
s = s[2:]
printSlice(s)
printSlice(sorig)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
//Output
//len=6 cap=6 [2 3 5 7 11 13]
//len=0 cap=6 []
//len=4 cap=6 [2 3 555 7]
//len=2 cap=4 [555 7]
//len=6 cap=6 [2 3 555 7 11 13]
How can I check if two slices are equal, given that the operators == and != are not an option?
package main
import "fmt"
func main() {
s1 := []int{1, 2}
s2 := []int{1, 2}
fmt.Println(s1 == s2)
}
This does not compile with:
invalid operation: s1 == s2 (slice can only be compared to nil)
You should use reflect.DeepEqual()
DeepEqual is a recursive relaxation of Go's == operator.
DeepEqual reports whether x and y are “deeply equal,” defined as
follows. Two values of identical type are deeply equal if one of the
following cases applies. Values of distinct types are never deeply
equal.
Array values are deeply equal when their corresponding elements are
deeply equal.
Struct values are deeply equal if their corresponding fields, both
exported and unexported, are deeply equal.
Func values are deeply equal if both are nil; otherwise they are not
deeply equal.
Interface values are deeply equal if they hold deeply equal concrete
values.
Map values are deeply equal if they are the same map object or if they
have the same length and their corresponding keys (matched using Go
equality) map to deeply equal values.
Pointer values are deeply equal if they are equal using Go's ==
operator or if they point to deeply equal values.
Slice values are deeply equal when all of the following are true: they
are both nil or both non-nil, they have the same length, and either
they point to the same initial entry of the same underlying array
(that is, &x[0] == &y[0]) or their corresponding elements (up to
length) are deeply equal. Note that a non-nil empty slice and a nil
slice (for example, []byte{} and []byte(nil)) are not deeply equal.
Other values - numbers, bools, strings, and channels - are deeply
equal if they are equal using Go's == operator.
You need to loop over each of the elements in the slice and test. Equality for slices is not defined. However, there is a bytes.Equal function if you are comparing values of type []byte.
func testEq(a, b []Type) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
This is just example using reflect.DeepEqual() that is given in #VictorDeryagin's answer.
package main
import (
"fmt"
"reflect"
)
func main() {
a := []int {4,5,6}
b := []int {4,5,6}
c := []int {4,5,6,7}
fmt.Println(reflect.DeepEqual(a, b))
fmt.Println(reflect.DeepEqual(a, c))
}
Result:
true
false
Try it in Go Playground
If you have two []byte, compare them using bytes.Equal. The Golang documentation says:
Equal returns a boolean reporting whether a and b are the same length and contain the same bytes. A nil argument is equivalent to an empty slice.
Usage:
package main
import (
"fmt"
"bytes"
)
func main() {
a := []byte {1,2,3}
b := []byte {1,2,3}
c := []byte {1,2,2}
fmt.Println(bytes.Equal(a, b))
fmt.Println(bytes.Equal(a, c))
}
This will print
true
false
And for now, here is https://github.com/google/go-cmp which
is intended to be a more powerful and safer alternative to reflect.DeepEqual for comparing whether two values are semantically equal.
package main
import (
"fmt"
"github.com/google/go-cmp/cmp"
)
func main() {
a := []byte{1, 2, 3}
b := []byte{1, 2, 3}
fmt.Println(cmp.Equal(a, b)) // true
}
You cannot use == or != with slices but if you can use them with the elements then Go 1.18 has a new function to easily compare two slices, slices.Equal:
Equal reports whether two slices are equal: the same length and all elements equal. If the lengths are different, Equal returns false. Otherwise, the elements are compared in increasing index order, and the comparison stops at the first unequal pair. Floating point NaNs are not considered equal.
The slices package import path is golang.org/x/exp/slices. Code inside exp package is experimental, not yet stable. It will be moved into the standard library in Go 1.19 eventually.
Nevertheless you can use it as soon as Go 1.18 (playground)
sliceA := []int{1, 2}
sliceB := []int{1, 2}
equal := slices.Equal(sliceA, sliceB)
fmt.Println(equal) // true
type data struct {
num float64
label string
}
sliceC := []data{{10.99, "toy"}, {500.49, "phone"}}
sliceD := []data{{10.99, "toy"}, {200.0, "phone"}}
equal = slices.Equal(sliceC, sliceD)
fmt.Println(equal) // true
If the elements of the slice don't allow == and !=, you can use slices.EqualFunc and define whatever comparator function makes sense for the element type.
In case that you are interested in writing a test, then github.com/stretchr/testify/assert is your friend.
Import the library at the very beginning of the file:
import (
"github.com/stretchr/testify/assert"
)
Then inside the test you do:
func TestEquality_SomeSlice (t * testing.T) {
a := []int{1, 2}
b := []int{2, 1}
assert.Equal(t, a, b)
}
The error prompted will be:
Diff:
--- Expected
+++ Actual
## -1,4 +1,4 ##
([]int) (len=2) {
+ (int) 1,
(int) 2,
- (int) 2,
(int) 1,
Test: TestEquality_SomeSlice
Thought of a neat trick and figured I'd share.
If what you are interested in knowing is whether two slices are identical (i.e. they alias the same region of data) instead of merely equal (the value at each index of one slice equals the value in the same index of the other) then you can efficiently compare them in the following way:
foo := []int{1,3,5,7,9,11,13,15,17,19}
// these two slices are exactly identical
subslice1 := foo[3:][:4]
subslice2 := foo[:7][3:]
slicesEqual := &subslice1[0] == &subslice2[0] &&
len(subslice1) == len(subslice2)
There are some caveats to this sort of comparison, in particular that you cannot compare empty slices in this way, and that the capacity of the slices isn't compared, so this "identicality" property is only really useful when reading from a slice or reslicing a strictly narrower subslice, as any attempt to grow the slice will be affected by the slices' capacity. Still, it's very useful to be able to efficiently declare, "these two huge blocks of memory are in fact the same block, yes or no."
To have a complete set of answers: here is a solution with generics.
func IsEqual[A comparable](a, b []A) bool {
// Can't be equal if length differs
if len(a) != len(b) {
return false
}
// Empty arrays trivially equal
if len(a) == 0 {
return true
}
// Two pointers going towards each other at every iteration
left := 0
right := len(a) - 1
for left < right {
if a[left] != b[left] || a[right] != b[right] {
return false
}
left++
right--
}
return true
}
Code uses strategy of "two pointers" which brings runtime complexity of n / 2, which is still O(n), however, twice as less steps than a linear check one-by-one.