Can anyone explain why the same code gives different results when written in Python 3 or Ruby?

During ProjectEuler's attempt to solve Issue 23 , I found a blog online and prepared the same version of the script in Python 3 and Ruby, just to check which one was faster.

Python 3 prints the answer, which is one of the Ruby answers - 1141

I modified the two scripts slightly to indicate the correct value, in which the two scripts start to have different behavior.

I cannot explain why.

This is a Ruby script:

def divisors(num)
  divisors = []
  (1...Math.sqrt(num)).each do |x|
    if num % x == 0
      divisors.push x
      temp = num/x
      divisors.push temp if temp != num && temp != x
    end
  end
  return divisors
end

def is_abundant?(num)
  sum = 0
  divisors(num).each {|x| sum += x}
  return sum > num
end

def main(limit)
  numbers = (1..limit).select {|number| is_abundant? number}
  sum = 0
  canbewrittenasabundantsum = []
  canbewrittenasabundantsum[limit]= nil

  numbers.each do |num1|

    numbers.each do |num2|

      if (num1 + num2) <= limit
        canbewrittenasabundantsum[num1+num2] = true
      else
        #break
      end
    end

  end

  (1...limit).each do |num|
    sum += num if canbewrittenasabundantsum[num].nil?
  end

  return sum
end

def prova
  puts("1140 -> #{main(1140)}")
  puts("1141 -> #{main(1141)}")
  puts("1142 -> #{main(1142)}")
  puts("1143 -> #{main(1143)}")
  puts("1144 -> #{main(1144)}")
  puts("1145 -> #{main(1145)}")
  puts("1146 -> #{main(1146)}")
  puts("1147 -> #{main(1147)}")
  puts("1148 -> #{main(1148)}")
  puts("1149 -> #{main(1149)}")
  puts("1150 -> #{main(1150)}")

  puts "#{divisors(1141).inspect}, #{is_abundant?(1141)}"
  puts "#{divisors(1142).inspect}, #{is_abundant?(1142)}"
end


prova

      

This is Python 3 one:

from math import sqrt

def divisors(num):
    divisors = []
    for x in range(1, int(sqrt(num))+1):
        if num % x == 0:
            divisors.append(x)
            temp = num//x
            if (temp != num and temp != x): divisors.append(num/x)
    return divisors

def is_abundant(num):
    return sum(divisors(num)) > num

def main(Limit):
    numbers = [x for x in range(1,Limit + 1) if is_abundant(x)]
    sums = 0
    canbewrittenasabundantsum = [False for x in range(0,Limit+1)]

    for num1 in numbers:
        for num2 in numbers:
            if (num1+num2) <= Limit:
                canbewrittenasabundantsum[num1+num2] = True
            else:
                break

    for num in range(1,Limit):
        if not canbewrittenasabundantsum[num]:
            sums += num
    return sums

def test():
    print("1140 -> {}".format(main(1140)))
    print("1141 -> {}".format(main(1141)))
    print("1142 -> {}".format(main(1142)))
    print("1143 -> {}".format(main(1143)))
    print("1144 -> {}".format(main(1144)))
    print("1145 -> {}".format(main(1145)))
    print("1146 -> {}".format(main(1146)))
    print("1147 -> {}".format(main(1147)))
    print("1148 -> {}".format(main(1148)))
    print("1149 -> {}".format(main(1149)))
    print("1150 -> {}".format(main(1150)))

    print(divisors(1141), is_abundant(1141))
    print(divisors(1142), is_abundant(1142))

test()

      

Can anyone explain to me why this is happening please? I've gone crazy!

PS. These are two ways out of scripting: Ruby:

1140 -> 280223
1141 -> 280223
1142 -> 281364
1143 -> 281364
1144 -> 281364
1145 -> 281364
1146 -> 281364
1147 -> 281364
1148 -> 282511
1149 -> 282511
1150 -> 282511
[1, 7, 163], false
[1, 2, 571], false

Process finished with exit code 0

      

Python:

1140 -> 280223
1141 -> 280223
1142 -> 280223
1143 -> 280223
1144 -> 280223
1145 -> 280223
1146 -> 280223
1147 -> 280223
1148 -> 281370
1149 -> 281370
1150 -> 281370
[1, 7, 163.0] False
[1, 2, 571.0] False

Process finished with exit code 0

      

So, starting in 1142, Python seems to be 1141 over Ruby, and I don't understand why this is the exact value! You can run these scripts up to 28123, and the ONLY value that gives a different result is! In the last two lines of the output you can see the divisors and the "is_abundant" result for 1141 and 1142 ... They are the same in both languages!

+3


source to share


1 answer


This is a great question IMHO because it covers languages ​​and even better languages ​​are very similar.

There are several things you can do to diagnose this:

  • Step through each program in the debugger and compare the output and variables at each step. If you are not used to the debugger, this is a great time to study because your question is perfect for it. (Or use the print instructions if you must :)

    # Ruby
    def proper_divisors(num)
      ...
      puts "proper divisors: #{num} #{divisors}"  # add this line
      return divisors
    
    # Python
    def proper_divisors(num):
      ...
      print "proper divisors:", num, divisors  # add this line 
      return divisors
    
          

  • Write unit tests for each method. In unit tests, make sure the inputs and outputs are what you expect them to be. For example, this unit test won't work on your Ruby code and it tells you that something is wrong in the method.

    # Presume your Ruby code file name is "sums.rb",
    # and your test code file name is "sums_test.rb",
    # and both files are saved in the same directory. 
    # Your run the test like this: ruby sums_test.rb 
    
    require_relative "sums"
    require "minitest/autorun"
    
    class TestEuler < Minitest::Test
      def test_proper_divisors
        # This is a smoke test: we pick a plausible input
        # that is valuable to our app, and see if it works.
        actual = proper_divisors(400)
        expect = [1, 2, 4, 5, 8, 10, 16, 20, 25, 40, 50, 80, 100, 200] 
        assert_equal expect.sort, actual.sort
      end
    end
    
          

  • Focus on where the error occurs by refactoring your code to use smaller methods. For example, transfer the first line of your main method to a new method, eg abundant_numbers

    .

    # Ruby
    def abundant_numbers(limit)
      (1..limit).select {|number| is_abundant? number}
    end
    
    # Python
    def abundant_numbers(Limit):
      [x for x in range(1,Limit + 1) if is_abundant(x)]
    
          

Update: There is an error in the method in the Ruby code divisors

, and here's a sample output:

 # Ruby output
 proper divisors: 400 [1, 2, 200, 4, 100, 5, 80, 8, 50, 10, 40, 16, 25]

 # Python output
 proper divisors: 400 [1, 2, 200, 4, 100, 5, 80, 8, 50, 10, 40, 16, 25, 20]

      

Note that the Ruby output is missing the final result 20.

The error in your Ruby code is the line:



 1...Math.sqrt(num)

      

The fix changes your three points to two points:

 1..Math.sqrt(num)

      

Ruby syntax says that two dots are up-to-inclusive, three dots are exclusive.

Example:

 (1..3).to_a   #=> [1, 2, 3]
 (1...3).to_a  #=> [1, 2]

      

Existing Ruby code checks all numbers up to but not including the square root.

+2


source







All Articles