Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Below is a Go solution to the problem 81 in project euler using uniform cost search to build a search tree. It works on the toy small problem but on the 80x80 matrix it runs out of space. Could anyone that is up for a challenge help improve this still using this solution. By the way, this is the first program of any consequence I have written in Go. I am try to learn the language

package main

import (
    "bufio"
    "container/heap"
    "fmt"
    "io"
    "os"
    "strconv"
    "strings"
)

var matrix [][]int = make([][]int, 0, 80)

func main() {
    f, _ := os.Open("matrix.txt")
    r := bufio.NewReader(f)
    defer f.Close()
    for {

        s, ok := r.ReadString('\n')
        if ok == io.EOF {
            break
        }
        s = strings.Trim(s, "\n")
        stringArr := strings.Split(s, ",")
        tmp := make([]int, 0)
        for i := 0; i < 80; i++ {
            v, _ := strconv.Atoi(stringArr[i])
            tmp = append(tmp, v)
        }
        matrix = append(matrix, tmp)
    }
    //fmt.Println(matrix)
    fmt.Println(uniformCostSearch(treeNode{0, 0, 4445, 0}))
}

func (node treeNode) getPriority(y, x int) int {
    return matrix[y][x]
}

type Node interface {
    // Neighbors returns a slice of vertices that are adjacent to v.
    Neighbors(v Node) []Node
}

// An treeNode is something we manage in a priority queue.
type treeNode struct {
    X        int
    Y        int
    priority int // The priority of the item in the queue.
    Index    int // The index of the item in the heap.
}

func (node treeNode) Neighbors() []*treeNode {
    tmp := []*treeNode{ //&treeNode{X: node.X - 1, Y: node.Y},
        &treeNode{X: node.X + 1, Y: node.Y},
        //&treeNode{X: node.X, Y: node.Y - 1},
        &treeNode{X: node.X, Y: node.Y + 1}}
    childNodes := make([]*treeNode, 0)
    for _, n := range tmp {
        if n.X >= 0 && n.Y >= 0 && n.X <= 80 && n.Y <= 80 {
            n.priority = node.priority + n.getPriority(n.Y, n.X)
            childNodes = append(childNodes, n)
        }
    }
    return childNodes
}

func uniformCostSearch(startNode treeNode) int {
    frontier := make(PriorityQueue, 0, 10000)
    closedSet := make([]*treeNode, 0)
    heap.Push(&frontier, &startNode)
    for frontier.Len() > 0 {
        fmt.Println(frontier.Len())
        currentNode := heap.Pop(&frontier).(*treeNode)
        if currentNode.X == 79 && currentNode.Y == 79 {
            return currentNode.priority
        } else {
            closedSet = append(closedSet, currentNode)
        }
        possibleMoves := currentNode.Neighbors()
        for _, move := range possibleMoves {
            explored := false
            for _, seen := range closedSet {
                if seen.X == move.X && seen.Y == move.Y {
                    explored = true
                    break
                }
            }
            if explored {
                continue
            }
            // check that frontier does not contain this node and 
            // if it does then compare the cost so far
            for index, node := range frontier {
                if node.X == move.X && node.Y == move.Y && move.priority < node.priority {
                    fmt.Println("removing")
                    heap.Remove(&frontier, index)
                    break
                }
            }
            heap.Push(&frontier, move)
        }
    }
    return -1
}

// A PriorityQueue implements heap.Interface and holds treeNodes.
type PriorityQueue []*treeNode

func (pq PriorityQueue) Len() int {
    return len(pq)
}

func (pq PriorityQueue) Less(i, j int) bool {
    // We want Pop to give us the lowest priority so we use greater than here.
    return pq[i].priority < pq[j].priority
}

func (pq PriorityQueue) Swap(i, j int) {
    pq[i], pq[j] = pq[j], pq[i]
    pq[i].Index = i
    pq[j].Index = j
}

func (pq *PriorityQueue) Push(x interface{}) {
    // Push and Pop use pointer receivers because they modify the slice's length,
    // not just its contents.
    // To simplify indexing expressions in these methods, we save a copy of the
    // slice object. We could instead write (*pq)[i].
    a := *pq
    n := len(a)
    a = a[0 : n+1]
    item := x.(*treeNode)
    item.Index = n
    a[n] = item
    *pq = a
}

func (pq *PriorityQueue) Pop() interface{} {
    a := *pq
    n := len(a)
    item := a[n-1]
    item.Index = -1 // for safety
    *pq = a[0 : n-1]
    return item
}
share|improve this question

1 Answer

Your code could be less fragile and more idiomatic. For example,

package main

import (
    "bufio"
    "container/heap"
    "errors"
    "fmt"
    "io"
    "os"
    "strconv"
    "strings"
)

type Matrix [][]int

type Node struct {
    x, y int
}

type Item struct {
    value    Node
    priority int
    index    int
}

type PriorityQueue []*Item

func (pq PriorityQueue) Len() int { return len(pq) }

func (pq PriorityQueue) Less(i, j int) bool {
    return pq[i].priority < pq[j].priority
}

func (pq PriorityQueue) Swap(i, j int) {
    pq[i], pq[j] = pq[j], pq[i]
    pq[i].index = i
    pq[j].index = j
}

func (pq *PriorityQueue) Push(x interface{}) {
    a := *pq
    n := len(a)
    item := x.(*Item)
    item.index = n
    a = append(a, item)
    *pq = a
}

func (pq *PriorityQueue) Pop() interface{} {
    a := *pq
    n := len(a)
    item := a[n-1]
    item.index = -1
    *pq = a[0 : n-1]
    return item
}

func (pq *PriorityQueue) changePriority(item *Item, priority int) {
    heap.Remove(pq, item.index)
    item.priority = priority
    heap.Push(pq, item)
}

func readMatrix() (Matrix, error) {
    var matrix Matrix
    file, err := os.Open(`matrix.txt`)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    rdr := bufio.NewReader(file)
    for {
        line, err := rdr.ReadString('\n')
        if err != nil {
            if err == io.EOF && len(line) == 0 {
                break
            }
            return nil, err
        }
        var row []int
        for _, s := range strings.Split(line[:len(line)-1], ",") {
            n, err := strconv.Atoi(s)
            if err != nil {
                return nil, err
            }
            row = append(row, n)
        }
        matrix = append(matrix, row)
    }
    return matrix, nil
}

func (i Item) neighbors(matrix Matrix) []*Item {
    items := make([]*Item, 0, 2)
    x, y, p := i.value.x, i.value.y, i.priority
    if x := x + 1; x < len(matrix[y]) {
        items = append(items, &Item{value: Node{x, y}, priority: p + matrix[y][x]})
    }
    if y := y + 1; y < len(matrix) {
        items = append(items, &Item{value: Node{x, y}, priority: p + matrix[y][x]})
    }
    return items
}

func UniformCostSearch(matrix Matrix) (int, error) {
    if len(matrix) < 0 || len(matrix[0]) < 0 {
        return 0, errors.New("UniformCostSearch: invalid root")
    }
    root := Item{value: Node{0, 0}, priority: matrix[0][0]}
    if len(matrix) < 0 || len(matrix[len(matrix)-1]) < 0 {
        return 0, errors.New("UniformCostSearch: invalid goal")
    }
    goal := Node{len(matrix[len(matrix)-1]) - 1, len(matrix) - 1}
    frontier := make(PriorityQueue, 0, len(matrix)*len(matrix[len(matrix)-1]))
    heap.Push(&frontier, &root)
    explored := make(map[Node]bool)
    for {
        if len(frontier) == 0 {
            return 0, errors.New("UniformCostSearch: frontier is empty")
        }
        item := heap.Pop(&frontier).(*Item)
        if item.value == goal {
            return item.priority, nil
        }
        explored[item.value] = true
    neighbor:
        for _, n := range item.neighbors(matrix) {
            if explored[n.value] {
                continue neighbor
            }
            for _, f := range frontier {
                if f.value == n.value {
                    if f.priority > n.priority {
                        frontier.changePriority(f, n.priority)
                    }
                    continue neighbor
                }
            }
            heap.Push(&frontier, n)
        }
    }
    return 0, errors.New("UniformCostSearch: unreachable")
}

func main() {
    matrix, err := readMatrix()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    minPathSum, err := UniformCostSearch(matrix)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println(minPathSum)
}
share|improve this answer
2  
Thanks you for this answer, but it would have been nice to explain the main changes that you did: it avoids the need to compare the two versions and trying to figure out the reasons of the changes. – Quentin Pradet Feb 12 at 8:44

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.