Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this a reasonable and idiomatic GoLang circular shift implementation?

Tags:

go

Can anyone comment on whether this is a reasonable and idiomatic way of implementing circular shift of integer arrays in Go? (I deliberately chose not to use bitwise operations.)

How could it be improved?

package main

import "fmt"

func main() {
    a := []int{1,2,3,4,5,6,7,8,9,10}
    fmt.Println(a)
    rotateR(a, 5)
    fmt.Println(a)
    rotateL(a, 5)
    fmt.Println(a)
}

func rotateL(a []int, i int) {
    for count := 1; count <= i; count++ {
        tmp := a[0]
        for n := 1;n < len(a);n++ {
            a[n-1] = a[n]
        }
        a[len(a)-1] = tmp
    }
}

func rotateR(a []int, i int) {
    for count := 1; count <= i; count++ {
        tmp := a[len(a)-1]
        for n := len(a)-2;n >=0 ;n-- {
            a[n+1] = a[n]
        }
        a[0] = tmp
    }
}
like image 826
ssh Avatar asked Mar 14 '23 12:03

ssh


1 Answers

Rotating the slice one position at a time, and repeating to get the total desired rotation means it will take time proportional to rotation distance × length of slice. By moving each element directly into its final position you can do this in time proportional to just the length of the slice.

The code for this is a little more tricky than you have, and you’ll need a GCD function to determine how many times to go through the slice:

func gcd(a, b int) int {
    for b != 0 {
        a, b = b, a % b
    }

    return a
}

func rotateL(a []int, i int) {

    // Ensure the shift amount is less than the length of the array,
    // and that it is positive.
    i = i % len(a)
    if i < 0 {
        i += len(a)
    }

    for c := 0; c < gcd(i, len(a)); c++ {

        t := a[c]

        j := c

        for {
            k := j + i
            // loop around if we go past the end of the slice
            if k >= len(a) {
                k -= len(a)
            }
            // end when we get to where we started
            if k == c {
                break
            }
            // move the element directly into its final position
            a[j] = a[k]
            j = k
        }

        a[j] = t
    }
}

Rotating a slice of size l right by p positions is equivalent to rotating it left by lp positions, so you can simplify your rotateR function by using rotateL:

func rotateR(a []int, i int) {
    rotateL(a, len(a) - i)
}
like image 166
matt Avatar answered Apr 08 '23 17:04

matt