-3

I need to optimize this code. Any suggestions to make it go faster, please tell me. I don't have a specific amount that I want it to go faster, any suggestion would be helpful. In terms of complexity I want to keep it below O(n^2)

I'm wondering if trying to convert the array that I'm using into like a set or hash because that is quicker right? How much faster in terms of complexity might this allow me to run?

The main problem I think might be my use of the ruby combination function which runs pretty slow, does anyone know exactly the complexity for this ruby function? is there a faster alternative to this?

the point of this code is basically to find the single point that is the shortest combined distance from all the other points ie (the friends house that is most convenient for everyone to go to). there is a little extra code here which has some debugging/printing functions.

class Point
    attr_accessor :x, :y, :distance, :done, :count

    def initialize(x,y)
        @x = x
        @y = y
        @distance = 0
        @closestPoint = []
        @done = false
        @count = 0
    end
end

class Edge
    attr_accessor :edge1, :edge2, :weight

    def initialize(edge1,edge2,weight)
      @edge1 = edge1
      @edge2 = edge2
      @weight = weight
    end
end

class AdjacencyList
    attr_accessor :name, :minSumList, :current

    def initialize(name)
      @name = name
      @minSumList = []
      @current = nil
      @vList = []
      @edgeList = []
    end

    def addVertex(vertex)
      @vList.push(vertex)
    end

    def generateEdges2
      minSumNode = nil
      current = nil
      last = nil

      @vList.combination(2) { |vertex1, vertex2|
        distance = distance2points(vertex1,vertex2)
        edge = Edge.new(vertex1,vertex2,distance)

        if (current == nil)
          current = vertex1
          minSumNode = vertex1
        end

        vertex1.distance += distance
        vertex2.distance += distance

        vertex1.count += 1
        vertex2.count += 1

        if (vertex1.count == @vList.length-1)
          vertex1.done = true
        elsif (vertex2.count == @vList.length-1)
           vertex2.done = true
        end

        if ((vertex1.distance < minSumNode.distance) && (vertex1.done == true))            
          minSumNode = vertex1
        end

        #@edgeList.push(edge)
       }

        return minSumNode.distance
    end

    def generateEdges
      @vList.combination(2) { |vertex1, vertex2| 
        distance = distance2points(vertex1,vertex2)
        @edgeList.push(Edge.new(vertex1,vertex2,distance))
      }
    end

    def printEdges
      @edgeList.each {|edge| puts "(#{edge.edge1.x},#{edge.edge1.y}) <=> (#{edge.edge2.x},#{edge.edge2.y}) weight: #{edge.weight}"}
    end

    def printDistances
      @vList.each {|v| puts "(#{v.x},#{v.y} distance = #{v.distance})"}
    end
end

def distance2points(point1,point2)
     xdistance = (point1.x - point2.x).abs
     ydistance = (point1.y - point2.y).abs

     total_raw = xdistance + ydistance
     return totaldistance = total_raw - [xdistance,ydistance].min
end

#pointtest1 = Point.new(0,1)
#pointtest2 = Point.new(2,5)
#pointtest3 = Point.new(3,1)
#pointtest4 = Point.new(4,0)

graph = AdjacencyList.new("graph1")

gets
while (line = gets)
    graph.addVertex(Point.new(line.split[0].to_i,line.split[1].to_i))
end

#graph.addVertex(pointtest1)
#graph.addVertex(pointtest2)
#graph.addVertex(pointtest3)
#graph.addVertex(pointtest4)

puts graph.generateEdges2
#graph.printEdges
#graph.printDistances
Evan
  • 5,975
  • 8
  • 34
  • 63
  • You mixed
     and . But you probably just want CTRL+K (4 spaces) to get nice highlighting.
    – diedthreetimes Aug 09 '11 at 20:45
  • Also, if you want someone to answer your question, may try asking a specific question. Just posting all of your code is burdensome to read, and hard to understand. A short paragraph about what you think the problem is, *and* ways you've tried to fix it will do you wonders ;) – diedthreetimes Aug 09 '11 at 20:48
  • i did have a short pg about the problem and things that I'd thought about. not good enough? also, in this case I thought it would be clearer to post all the code – Evan Aug 09 '11 at 20:50
  • 2
    *"I'm looking to optimize my code because I think it is running too slow"* - You *think*? You need to do more than guess. Measure it and, if it is indeed too slow, come back and tell us where the bottleneck is, although by that point you will likely be able to fix it on your own. – Ed S. Aug 09 '11 at 20:54
  • Your paragraph is very vague, and doesn't really explain the problem. I agree that in this case all the code may be necessary, but it doesn't replace a concise explanation of the problem. It allows a potential answerer to get the 'gist' of the code without having to translate it all. As an example, what do you mean by 'too slow'? Have you run any benchmarks? How fast do you expect it to run? etc. As a general rule, the more code you think the question requires, the more paragraphs of explanation you should have. – diedthreetimes Aug 09 '11 at 20:54
  • One final note. This is a Q&A site. "Optimize this ruby code" is not a question. – diedthreetimes Aug 09 '11 at 20:56
  • i clearly have specific questions in my paragraph. thanks for all the "help" – Evan Aug 09 '11 at 21:02
  • sorry should have said... "how can i optimize this code?" derp – Evan Aug 09 '11 at 21:03
  • Right, and again, you don't start optimizing by randomly changing things until they "feel" faster. Since you're obviously new to this I would lookup common profiling techniques in Ruby and take the advice we are giving you. You think it is not helpful but in reality it is far more helpful than handing you a revised chunk of code given the fact that you likely will not even know what changes actually improved anything. Aside from that, you don't give us enough info to do it even if we wanted to. – Ed S. Aug 09 '11 at 21:13
  • Hey come on now, you have to give me some credit. I did "help" format your code :P – diedthreetimes Aug 09 '11 at 23:00
  • "ANY SUGGESTION WOULD BE HELPFUL" - here's one: don't use all caps. – Andrew Grimm Aug 10 '11 at 04:37
  • @diedthreetimes your dynamic programming comment was helpful – Evan Aug 10 '11 at 13:27

3 Answers3

2

Try to do this, and then post some more code:

ruby -rprofile your_script your_args

This will run the script under the profiler, and generate a nice table with results. If you post that here, it's more likely to get better help. Plus, you will have a more exact idea of what's consuming your CPU cycles.

Geo
  • 93,257
  • 117
  • 344
  • 520
1

Sets are basically hashes, and the advantage of hashes over arrays is O(1) find operations. Since you are simply iterating over the entire array, hashes will not offer any speed improvements if you simply replace the arrays with hashes.

Your real problem is that the running time of your algorithm is O(n^2), as in given a set of n points it will have to perform n^2 operations since you're matching every point with every other possible point.

This can be somewhat improved using hashes to cache values. For example, lets say you want the distance between point "a" and point "b". You could have a hash @distances which stores @distances["a,b"] = 52 (of course you'll have to be smart about what to use as the key). Basically just try to remove redundant operations wherever you can.

That said, the largest speed boost would be from a smarter algorithm, but I can't think of something applicable off the top of my head right now.

Karl
  • 6,035
  • 5
  • 30
  • 39
0

There's something many people know, and it won't cost you anything.

While you're trying to guess how to make the code faster, or scouring the internet for some kind of profiler, just run the program under the debugger and interrupt it while it's being slow.

Do it several times, and each time take careful note of what it's doing and why.

Here's an example in python.

The slower it is, the more obvious the problem will be.

Community
  • 1
  • 1
Mike Dunlavey
  • 40,059
  • 14
  • 91
  • 135