Hi All, 
Last year, I had asked something similar about comparing QuickUnion with 
QuickFind although that turned out to be a flaw in my benchmarking function.

This time, I am comparing the QuickUnion with WeightedQuickUnion.
I ran benchmarking on this code and noticed that the WeightedQuickUnion 
actually runs slower than the QuickUnion (contrary to my understanding). 

I can confirm that my implementation of the algorithms is correct.

*So I am not able to understand why QuickUnion performs better than 
WeightedQuickUnion ?*

I believe that the difference is probably because in WeightedQuickUnion, 
there is a lot of time spent in comparing the sizes of 2 nodes and then 
attaching the smaller sized tree to the larger sized tree. I was able to 
check this by performing profiling and here is what I see. 
WeighteQuickUnion spends about 750ms in performing the comparisons and 
updating the array elements on line numbers 85-90.

(pprof) list WeightedQuickUnion
Total: 4.26s
ROUTINE ======================== ds.(*DynamicConnector).WeightedQuickUnion 
in /Users/neeraj.vaidyamatrixx.com/Work/Matrixx/src/ds/uf.go
     1.26s      2.27s (flat, cum) 53.29% of Total
         .          .     72:    // Union
         .          .     73:    dc.elements[rootp] = dc.elements[rootq]
         .          .     74:    return false
         .          .     75:}
         .          .     76:
     130ms      130ms     77:func (dc *DynamicConnector) 
WeightedQuickUnion(p, q int) bool {
         .          .     78:    // Find
      40ms      1.04s     79:    rootp, rootq := dc.getRoot(p), 
dc.getRoot(q)
     150ms      150ms     80:    if rootp == rootq {
         .          .     81:        return true
         .          .     82:    }
         .          .     83:    // Union
         .          .     84:    // update root depending on which tree is 
bigger
     





*280ms      280ms     85:    if dc.nodeWeight[rootp] <= 
dc.nodeWeight[rootq] {         .          .     86:        
dc.elements[rootp] = rootq      40ms       40ms     87:        
dc.nodeWeight[rootq] += dc.nodeWeight[rootp]         .          .     
88:    } else {      10ms       10ms     89:        dc.elements[rootq] = 
rootp     410ms      420ms     90:        dc.nodeWeight[rootp] += 
dc.nodeWeight[rootq]         .          .     91:    }*
     200ms      200ms     92:    return false
         .          .     93:}
         .          .     94:
         .          .     95:func (dc *DynamicConnector) 
WeightedQuickUnionWithPathCompression(p, q int) bool {
         .          .     96:    return false
         .          .     97:}


Given below is all my code.

My benchmarking functions :
*uf_test.go*

package ds

import (
"fmt"
"testing"
)

var elementsMap map[int][]int
var nodeWeightsMap map[int][]int

func init() {
elementsMap = map[int][]int{
10: make([]int, 10),
1000: make([]int, 1000),
100000: make([]int, 100000),
1000000: make([]int, 1000000),
}

for _, v := range elementsMap {
initElementsArray(v)
}

nodeWeightsMap = map[int][]int{
10: make([]int, 10),
1000: make([]int, 1000),
100000: make([]int, 100000),
1000000: make([]int, 1000000),
}

for _, v := range nodeWeightsMap {
initWeightsArray(v)
}
}

func initElementsArray(elems []int) {
for i := range elems {
elems[i] = i
}
}

func initWeightsArray(elems []int) {
for i := range elems {
elems[i] = 1
}
}

func BenchmarkQuickUnion(b *testing.B) {
testData := []DynamicConnector{
NewDynamicConnector(QuickUnion, 10),
NewDynamicConnector(QuickUnion, 1000),
NewDynamicConnector(QuickUnion, 100000),
}
for _, dc := range testData {
b.Run(fmt.Sprintf("Size-%d", dc.size), func(b *testing.B) {
for i := 0; i < b.N; i++ {
for j := 1; j < dc.size-1; j++ {
if dc.connect(j, j+1) {
b.Errorf("error")
}
}
if dc.connect(0, 1) {
b.Errorf("error")
}
copy(dc.elements, elementsMap[dc.size])
}
})
}
}

func BenchmarkWeightedQuickUnion(b *testing.B) {
testData := []DynamicConnector{
NewDynamicConnector(WeightedQuickUnion, 10),
NewDynamicConnector(WeightedQuickUnion, 1000),
NewDynamicConnector(WeightedQuickUnion, 100000),
}

for _, dc := range testData {
b.Run(fmt.Sprintf("Size-%d", dc.size), func(b *testing.B) {
for i := 0; i < b.N; i++ {
for j := 1; j < dc.size-1; j++ {
if dc.connect(j, j+1) {
b.Errorf("error")
}
}
if dc.connect(0, 1) {
b.Errorf("error")
}
copy(dc.elements, elementsMap[dc.size])
copy(dc.nodeWeight, nodeWeightsMap[dc.size])
}
})
}
}



My code is as follows :

*uf.go*

package ds

type DynamicConnector struct {
elements []int
nodeWeight []int
typ ConnectorType
size int
connect func(p, q int) bool
}

type ConnectorType int

const (
QuickFind ConnectorType = iota
QuickUnion
WeightedQuickUnion
WeightedQuickUnionWithPathCompression
)

func NewDynamicConnector(connectorType ConnectorType, size int) 
DynamicConnector {
dynamicConnector := DynamicConnector{typ: connectorType, size: size}
dynamicConnector.elements = make([]int, size)
// initialize
for i := range dynamicConnector.elements {
dynamicConnector.elements[i] = i
}
switch connectorType {
case WeightedQuickUnion, WeightedQuickUnionWithPathCompression:
dynamicConnector.nodeWeight = make([]int, size)
for i := range dynamicConnector.nodeWeight {
dynamicConnector.nodeWeight[i] = 1
}
}

if connectorType == QuickFind {
dynamicConnector.connect = dynamicConnector.QuickFind
} else if connectorType == QuickUnion {
dynamicConnector.connect = dynamicConnector.QuickUnion
} else if connectorType == WeightedQuickUnion {
dynamicConnector.connect = dynamicConnector.WeightedQuickUnion
} else if connectorType == WeightedQuickUnionWithPathCompression {
dynamicConnector.connect = 
dynamicConnector.WeightedQuickUnionWithPathCompression
}

return dynamicConnector
}

func (dc *DynamicConnector) Connect(p, q int) bool {
return dc.connect(p, q)
}

func (dc *DynamicConnector) QuickFind(p, q int) bool {
// Find
if dc.elements[p] == dc.elements[q] {
return true
}
// Union
for t, i := dc.elements[p], 0; i < dc.size; i++ {
if dc.elements[i] == t {
dc.elements[i] = dc.elements[q]
}
}
return false
}
// QuickUnion returns true if nodes are already connected and false 
otherwise
func (dc *DynamicConnector) QuickUnion(p, q int) bool {
// Find
rootp, rootq := dc.getRoot(p), dc.getRoot(q)
if rootp == rootq {
return true
}
// Union
dc.elements[rootp] = rootq
return false
}

// WeightedQuickUnion returns true if nodes are already connected and false 
otherwise
func (dc *DynamicConnector) WeightedQuickUnion(p, q int) bool {
// Find
rootp, rootq := dc.getRoot(p), dc.getRoot(q)
if rootp == rootq {
return true
}
// Union
// update root depending on which tree is bigger
if dc.nodeWeight[rootp] <= dc.nodeWeight[rootq] {
dc.elements[rootp] = rootq
dc.nodeWeight[rootq] += dc.nodeWeight[rootp]
} else {
dc.elements[rootq] = rootp
dc.nodeWeight[rootp] += dc.nodeWeight[rootq]
}
return false
}

func (dc *DynamicConnector) WeightedQuickUnionWithPathCompression(p, q int) 
bool {
return false
}

func (dc *DynamicConnector) getRoot(p int) int {
root := p
for ; dc.elements[root] != root; root = dc.elements[root] {
}
return root
}


-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/5b80484d-4e79-4964-98a4-95458010d6f5n%40googlegroups.com.

Reply via email to