Cawb07
Cawb07

Reputation: 2127

Why is elif running before if statement? Python

Project Euler problem 33 asks solvers to find "curious fractions": fractions which are equal to the fraction obtained by deleting digits that appear in both numerator and denominator (for example, 49/98, which "cancels" to 4/8 by deleting the 9s).

NOTE: This is not a competition to solve the Project Euler problem FASTEST. I already rewrote this code into a solution that's as fast as Nickie's. This is done by removing the for loop and if float(numer)... conditionals. It looks like this:

def curious(numer,denom):
    n = str(numer)
    d = str(denom)

    if len(n) == 2 and len(d) == 2:
        if n[1] == d[0]:
            if n != d:
                if d[1] != "0":
                    if Fraction(int(n[0]), int(d[1])) \
                    == \
                    Fraction(numer, denom):
                            return n + d

I still don't understand the conditional problem though.

I am not new to Python programming and this is the first time I've encountered anything like this. In the function defined below, there are paired if and elif statements:

import time
from fractions import Fraction

def curious(numer,denom):
    n = str(numer)
    d = str(denom)

    if len(n) == 2 and len(d) == 2:
        if n[1] == d[0]:
            for i in range(2, numer+1):
                if float(numer)/i == float(int(n[0])) \
                and float(denom)/i == float(int(d[1])):
                    if n != d:
                        return n + "/" + d

                elif d[1] != "0":
                    if Fraction(int(n[0]), int(d[1])) \
                    == \
                    Fraction(numer, denom):
                        if n != d:
                            return n + d


start = time.time()

nums = []
denoms = []
for i in range(10, 100):
    for j in range(10, 100):
        if i < j:
            if type(curious(i, j)) == str:
                frac = (curious(i,j))
                nums.append(frac[0]+frac[1])
                denoms.append(frac[2]+frac[3])
                print frac

pro = 1
duct = 1

for i in range(0, len(nums)):
    pro *= int(nums[i])
    duct *= int(denoms[i])


print Fraction(pro, duct)

elapsed = time.time() - start
print "The elapsed time is %s seconds." % (elapsed)

when I run the code with the elif statement commented out, the result is this:

16/64,
19/95,
26/65,
The elapsed time is 0.0500001907349 seconds.

But when the elif is included, the if statement is ignored and the result is this:

1664,
1995,
2665,
4998,
The elapsed time is 0.59700012207 seconds.

It appears that Python prefers to take the inefficient route, even though the conditions of the if statement are satisfied in both cases. Can anyone help me solve this mystery?

Ideally, I want the result to be:

16/64,
19/95,
26/65,
4998,
The elapsed time is 0.0500001907349 seconds.

Upvotes: 1

Views: 431

Answers (2)

eyquem
eyquem

Reputation: 27575

In my following code, the elif section is active or deactivated according to the value given to ELIF .

import time
from fractions import Fraction

def curious(numer,denom,disp=None):
    n = str(numer)
    d = str(denom)
    fracs = []
    if n!=d and n[1] == d[0]:
        ELIF = 1

        fn0 = float(n[0])
        fd1 = float(d[1])
        if disp:
            print ('---------------------------------------\n'
                   'n == %s  d == %s\n'
                   "float('%s'[0]) == %f  float('%s'[1]) == %f"
                   % ( n,d,n,fn0,d,fd1 ) )

        for i in range(2, numer+1):
            if disp:
                print ('i== %2d   '
                       '%s./%2d==%10f   %s./%2d==%10f  %s' %
                       (i,n,i,float(n)/i,d,i,float(d)/i,
                        float(n)/i==fn0 and float(d)/i==fd1) )

            if float(numer)/i==fn0 and float(denom)/i==fd1:
                if disp:
                    print ('  %s\n'
                           '  - %s/%s returned by if section -' %
                           ('- elif part is active -' if ELIF
                            else '- elif part is not active -',
                            n,d))
                return n + "/" + d

            elif ELIF and d[1] != "0":
                if Fraction( n[0] + '/' + d[1] ) \
                   == \
                   Fraction(numer, denom):
                    if disp:
                        print ('  %s\n'
                               "  Fraction('%s/%s') == %s\n"
                               "  Fraction(%s, %s) == %s\n"
                               '  - %s%s returned by elif section -' %
                               ('- elif part is active -' if ELIF
                                else '- elif part is not active -',
                                n[0],d[1],Fraction( n[0] + '/' + d[1] ),
                                numer,denom,Fraction(numer, denom),n,d) )
                    return n + d

        else:
            if n+d=='4998':
                if disp:
                    print ('  - elif part is active -' if ELIF
                           else '  - elif part is not active -')
                    print '  - No convenient divisor found for 49 98 : returned by else section -'
                return 'No convenient divisor found for 49 98'

start = time.time()

nums,denoms = [],[]
fracs = []
for i in range(10, 100):
    for j in range(10, 100):
        if curious(i,j):
            frac = (curious(i,j,'display'))
            print('frac == %s' % frac)
            fracs.append(frac)
            deb,end = frac[0:2],frac[-2:]
            try:
                int(deb)
                nums.append(deb)
                denoms.append(end)
            except:
                print "can't record %r==frac[0:2] as numerator" % (deb,)

print '\ndetected :\n  %s' % '\n  '.join(fracs)

pro,duct = 1,1
print '\nnums ==',nums
print 'denoms ==',denoms

for i in range(0, len(nums)):
    pro *= int(nums[i])
    duct *= int(denoms[i])
print 'pro,duct ==',pro,duct
print 'Fraction(pro, duct) ==',Fraction(pro, duct)

elapsed = time.time() - start
print "The elapsed time is %s seconds." % (elapsed)

result with ELIF = 0

---------------------------------------
n == 16  d == 64
float('16'[0]) == 1.000000  float('64'[1]) == 4.000000
i==  2   16./ 2==  8.000000   64./ 2== 32.000000  False
i==  3   16./ 3==  5.333333   64./ 3== 21.333333  False
i==  4   16./ 4==  4.000000   64./ 4== 16.000000  False
i==  5   16./ 5==  3.200000   64./ 5== 12.800000  False
i==  6   16./ 6==  2.666667   64./ 6== 10.666667  False
i==  7   16./ 7==  2.285714   64./ 7==  9.142857  False
i==  8   16./ 8==  2.000000   64./ 8==  8.000000  False
i==  9   16./ 9==  1.777778   64./ 9==  7.111111  False
i== 10   16./10==  1.600000   64./10==  6.400000  False
i== 11   16./11==  1.454545   64./11==  5.818182  False
i== 12   16./12==  1.333333   64./12==  5.333333  False
i== 13   16./13==  1.230769   64./13==  4.923077  False
i== 14   16./14==  1.142857   64./14==  4.571429  False
i== 15   16./15==  1.066667   64./15==  4.266667  False
i== 16   16./16==  1.000000   64./16==  4.000000  True
  - elif part is not active -
  - 16/64 returned by if section -
frac == 16/64
---------------------------------------
n == 19  d == 95
float('19'[0]) == 1.000000  float('95'[1]) == 5.000000
i==  2   19./ 2==  9.500000   95./ 2== 47.500000  False
i==  3   19./ 3==  6.333333   95./ 3== 31.666667  False
i==  4   19./ 4==  4.750000   95./ 4== 23.750000  False
i==  5   19./ 5==  3.800000   95./ 5== 19.000000  False
i==  6   19./ 6==  3.166667   95./ 6== 15.833333  False
i==  7   19./ 7==  2.714286   95./ 7== 13.571429  False
i==  8   19./ 8==  2.375000   95./ 8== 11.875000  False
i==  9   19./ 9==  2.111111   95./ 9== 10.555556  False
i== 10   19./10==  1.900000   95./10==  9.500000  False
i== 11   19./11==  1.727273   95./11==  8.636364  False
i== 12   19./12==  1.583333   95./12==  7.916667  False
i== 13   19./13==  1.461538   95./13==  7.307692  False
i== 14   19./14==  1.357143   95./14==  6.785714  False
i== 15   19./15==  1.266667   95./15==  6.333333  False
i== 16   19./16==  1.187500   95./16==  5.937500  False
i== 17   19./17==  1.117647   95./17==  5.588235  False
i== 18   19./18==  1.055556   95./18==  5.277778  False
i== 19   19./19==  1.000000   95./19==  5.000000  True
  - elif part is not active -
  - 19/95 returned by if section -
frac == 19/95
---------------------------------------
n == 26  d == 65
float('26'[0]) == 2.000000  float('65'[1]) == 5.000000
i==  2   26./ 2== 13.000000   65./ 2== 32.500000  False
i==  3   26./ 3==  8.666667   65./ 3== 21.666667  False
i==  4   26./ 4==  6.500000   65./ 4== 16.250000  False
i==  5   26./ 5==  5.200000   65./ 5== 13.000000  False
i==  6   26./ 6==  4.333333   65./ 6== 10.833333  False
i==  7   26./ 7==  3.714286   65./ 7==  9.285714  False
i==  8   26./ 8==  3.250000   65./ 8==  8.125000  False
i==  9   26./ 9==  2.888889   65./ 9==  7.222222  False
i== 10   26./10==  2.600000   65./10==  6.500000  False
i== 11   26./11==  2.363636   65./11==  5.909091  False
i== 12   26./12==  2.166667   65./12==  5.416667  False
i== 13   26./13==  2.000000   65./13==  5.000000  True
  - elif part is not active -
  - 26/65 returned by if section -
frac == 26/65
---------------------------------------
n == 49  d == 98
float('49'[0]) == 4.000000  float('98'[1]) == 8.000000
i==  2   49./ 2== 24.500000   98./ 2== 49.000000  False
i==  3   49./ 3== 16.333333   98./ 3== 32.666667  False
i==  4   49./ 4== 12.250000   98./ 4== 24.500000  False
i==  5   49./ 5==  9.800000   98./ 5== 19.600000  False
i==  6   49./ 6==  8.166667   98./ 6== 16.333333  False
i==  7   49./ 7==  7.000000   98./ 7== 14.000000  False
i==  8   49./ 8==  6.125000   98./ 8== 12.250000  False
i==  9   49./ 9==  5.444444   98./ 9== 10.888889  False
i== 10   49./10==  4.900000   98./10==  9.800000  False
i== 11   49./11==  4.454545   98./11==  8.909091  False
i== 12   49./12==  4.083333   98./12==  8.166667  False
i== 13   49./13==  3.769231   98./13==  7.538462  False
i== 14   49./14==  3.500000   98./14==  7.000000  False
i== 15   49./15==  3.266667   98./15==  6.533333  False
i== 16   49./16==  3.062500   98./16==  6.125000  False
i== 17   49./17==  2.882353   98./17==  5.764706  False
i== 18   49./18==  2.722222   98./18==  5.444444  False
i== 19   49./19==  2.578947   98./19==  5.157895  False
i== 20   49./20==  2.450000   98./20==  4.900000  False
i== 21   49./21==  2.333333   98./21==  4.666667  False
i== 22   49./22==  2.227273   98./22==  4.454545  False
i== 23   49./23==  2.130435   98./23==  4.260870  False
i== 24   49./24==  2.041667   98./24==  4.083333  False
i== 25   49./25==  1.960000   98./25==  3.920000  False
i== 26   49./26==  1.884615   98./26==  3.769231  False
i== 27   49./27==  1.814815   98./27==  3.629630  False
i== 28   49./28==  1.750000   98./28==  3.500000  False
i== 29   49./29==  1.689655   98./29==  3.379310  False
i== 30   49./30==  1.633333   98./30==  3.266667  False
i== 31   49./31==  1.580645   98./31==  3.161290  False
i== 32   49./32==  1.531250   98./32==  3.062500  False
i== 33   49./33==  1.484848   98./33==  2.969697  False
i== 34   49./34==  1.441176   98./34==  2.882353  False
i== 35   49./35==  1.400000   98./35==  2.800000  False
i== 36   49./36==  1.361111   98./36==  2.722222  False
i== 37   49./37==  1.324324   98./37==  2.648649  False
i== 38   49./38==  1.289474   98./38==  2.578947  False
i== 39   49./39==  1.256410   98./39==  2.512821  False
i== 40   49./40==  1.225000   98./40==  2.450000  False
i== 41   49./41==  1.195122   98./41==  2.390244  False
i== 42   49./42==  1.166667   98./42==  2.333333  False
i== 43   49./43==  1.139535   98./43==  2.279070  False
i== 44   49./44==  1.113636   98./44==  2.227273  False
i== 45   49./45==  1.088889   98./45==  2.177778  False
i== 46   49./46==  1.065217   98./46==  2.130435  False
i== 47   49./47==  1.042553   98./47==  2.085106  False
i== 48   49./48==  1.020833   98./48==  2.041667  False
i== 49   49./49==  1.000000   98./49==  2.000000  False
  - elif part is not active -
  - No convenient divisor found for 49 98 : returned by else section -
frac == No convenient divisor found for 49 98
can't record 'No'==frac[0:2] as numerator

detected :
  16/64
  19/95
  26/65
  No convenient divisor found for 49 98

nums == ['16', '19', '26']
denoms == ['64', '95', '65']
pro,duct == 7904 395200
Fraction(pro, duct) == 1/50
The elapsed time is 2.40599989891 seconds.

result with ELIF = 1

---------------------------------------
n == 16  d == 64
float('16'[0]) == 1.000000  float('64'[1]) == 4.000000
i==  2   16./ 2==  8.000000   64./ 2== 32.000000  False
  - elif part is active -
  Fraction('1/4') == 1/4
  Fraction(16, 64) == 1/4
  - 1664 returned by elif section -
frac == 1664
---------------------------------------
n == 19  d == 95
float('19'[0]) == 1.000000  float('95'[1]) == 5.000000
i==  2   19./ 2==  9.500000   95./ 2== 47.500000  False
  - elif part is active -
  Fraction('1/5') == 1/5
  Fraction(19, 95) == 1/5
  - 1995 returned by elif section -
frac == 1995
---------------------------------------
n == 26  d == 65
float('26'[0]) == 2.000000  float('65'[1]) == 5.000000
i==  2   26./ 2== 13.000000   65./ 2== 32.500000  False
  - elif part is active -
  Fraction('2/5') == 2/5
  Fraction(26, 65) == 2/5
  - 2665 returned by elif section -
frac == 2665
---------------------------------------
n == 49  d == 98
float('49'[0]) == 4.000000  float('98'[1]) == 8.000000
i==  2   49./ 2== 24.500000   98./ 2== 49.000000  False
  - elif part is active -
  Fraction('4/8') == 1/2
  Fraction(49, 98) == 1/2
  - 4998 returned by elif section -
frac == 4998

detected :
  1664
  1995
  2665
  4998

nums == ['16', '19', '26', '49']
denoms == ['64', '95', '65', '98']
pro,duct == 387296 38729600
Fraction(pro, duct) == 1/100
The elapsed time is 6.40599989891 seconds.

When ELIF is False,
the result 16/64 19/95 26/65 is returned by the if section, the format betrays this origin.

When ELIF is True,
the result 1664 1995 2665 4998 is returned by the elif section, it's revealed by the format too.
The fact that it's returned by the elif section makes you conclude that the if section isn't run.

Your conclusion is false because you erroneously think that

"the conditions of the if statement are satisfied in both cases"

No, they aren't, since you doesn't allow the loop to test all the possible values for i: for the first value i==2 the result of the test is always False, then the elif section, is always entered after this first value (nikie has already explained this).
The values of the input are the same but the conditions necessary to bring the evidence it's the same are not fulfilled.

To give the loop the possibility to run completely, it is necessary to unindent the elif section, .
Remark that in the following code , appending frac[0:2] in nums and frac[-2:] in denoms allows the execution to not stop because of error, and that the result is what you were waiting: 16/64 19/95 26/65 4998

def curious(numer,denom,disp=None):
    n = str(numer)
    d = str(denom)
    fracs = []
    if n!=d and n[1] == d[0]:
        ELIF = 1

        fn0 = float(n[0])
        fd1 = float(d[1])
        if disp:
            print ('---------------------------------------\n'
                   'n == %s  d == %s\n'
                   "float('%s'[0]) == %f  float('%s'[1]) == %f"
                   % ( n,d,n,fn0,d,fd1 ) )

        for i in range(2, numer+1):
            if disp:
                print ('i== %2d   '
                       '%s./%2d==%10f   %s./%2d==%10f  %s' %
                       (i,n,i,float(n)/i,d,i,float(d)/i,
                        float(n)/i==fn0 and float(d)/i==fd1) )
            if float(numer)/i==fn0 and float(denom)/i==fd1:
                if disp:
                    print ('  %s\n'
                           '  - %s/%s returned by if section -' %
                           ('- elif part is active -' if ELIF
                            else '- elif part is not active -',
                            n,d))
                return n + "/" + d

        if ELIF and d[1] != "0":
            if Fraction( n[0] + '/' + d[1] ) \
               == \
               Fraction(numer, denom):
                if disp:
                    print ('  %s\n'
                           "  Fraction('%s/%s') == %s\n"
                           "  Fraction(%s, %s) == %s\n"
                           '  - %s%s returned by elif section -' %
                           ('- elif part is active -' if ELIF
                            else '- elif part is not active -',
                            n[0],d[1],Fraction( n[0] + '/' + d[1] ),
                            numer,denom,Fraction(numer, denom),n,d) )
                return n + d

        else:
            if n+d=='4998':
                if disp:
                    print ('  - elif part is active -' if ELIF
                           else '  - elif part is not active -')
                    print '  - No convenient divisor found for 49 98 : returned by else section -'
                return 'No convenient divisor found for 49 98'

displays

(skiped lines)
---------------------------------------
n == 26  d == 65
float('26'[0]) == 2.000000  float('65'[1]) == 5.000000
i==  2   26./ 2== 13.000000   65./ 2== 32.500000  False
i==  3   26./ 3==  8.666667   65./ 3== 21.666667  False
i==  4   26./ 4==  6.500000   65./ 4== 16.250000  False
i==  5   26./ 5==  5.200000   65./ 5== 13.000000  False
i==  6   26./ 6==  4.333333   65./ 6== 10.833333  False
i==  7   26./ 7==  3.714286   65./ 7==  9.285714  False
i==  8   26./ 8==  3.250000   65./ 8==  8.125000  False
i==  9   26./ 9==  2.888889   65./ 9==  7.222222  False
i== 10   26./10==  2.600000   65./10==  6.500000  False
i== 11   26./11==  2.363636   65./11==  5.909091  False
i== 12   26./12==  2.166667   65./12==  5.416667  False
i== 13   26./13==  2.000000   65./13==  5.000000  True
  - elif part is active -
  - 26/65 returned by if section -
frac == 26/65
---------------------------------------
n == 49  d == 98
float('49'[0]) == 4.000000  float('98'[1]) == 8.000000
i==  2   49./ 2== 24.500000   98./ 2== 49.000000  False
i==  3   49./ 3== 16.333333   98./ 3== 32.666667  False
i==  4   49./ 4== 12.250000   98./ 4== 24.500000  False
i==  5   49./ 5==  9.800000   98./ 5== 19.600000  False
i==  6   49./ 6==  8.166667   98./ 6== 16.333333  False
i==  7   49./ 7==  7.000000   98./ 7== 14.000000  False
i==  8   49./ 8==  6.125000   98./ 8== 12.250000  False
i==  9   49./ 9==  5.444444   98./ 9== 10.888889  False
i== 10   49./10==  4.900000   98./10==  9.800000  False
i== 11   49./11==  4.454545   98./11==  8.909091  False
i== 12   49./12==  4.083333   98./12==  8.166667  False
i== 13   49./13==  3.769231   98./13==  7.538462  False
i== 14   49./14==  3.500000   98./14==  7.000000  False
i== 15   49./15==  3.266667   98./15==  6.533333  False
i== 16   49./16==  3.062500   98./16==  6.125000  False
i== 17   49./17==  2.882353   98./17==  5.764706  False
i== 18   49./18==  2.722222   98./18==  5.444444  False
i== 19   49./19==  2.578947   98./19==  5.157895  False
i== 20   49./20==  2.450000   98./20==  4.900000  False
i== 21   49./21==  2.333333   98./21==  4.666667  False
i== 22   49./22==  2.227273   98./22==  4.454545  False
i== 23   49./23==  2.130435   98./23==  4.260870  False
i== 24   49./24==  2.041667   98./24==  4.083333  False
i== 25   49./25==  1.960000   98./25==  3.920000  False
i== 26   49./26==  1.884615   98./26==  3.769231  False
i== 27   49./27==  1.814815   98./27==  3.629630  False
i== 28   49./28==  1.750000   98./28==  3.500000  False
i== 29   49./29==  1.689655   98./29==  3.379310  False
i== 30   49./30==  1.633333   98./30==  3.266667  False
i== 31   49./31==  1.580645   98./31==  3.161290  False
i== 32   49./32==  1.531250   98./32==  3.062500  False
i== 33   49./33==  1.484848   98./33==  2.969697  False
i== 34   49./34==  1.441176   98./34==  2.882353  False
i== 35   49./35==  1.400000   98./35==  2.800000  False
i== 36   49./36==  1.361111   98./36==  2.722222  False
i== 37   49./37==  1.324324   98./37==  2.648649  False
i== 38   49./38==  1.289474   98./38==  2.578947  False
i== 39   49./39==  1.256410   98./39==  2.512821  False
i== 40   49./40==  1.225000   98./40==  2.450000  False
i== 41   49./41==  1.195122   98./41==  2.390244  False
i== 42   49./42==  1.166667   98./42==  2.333333  False
i== 43   49./43==  1.139535   98./43==  2.279070  False
i== 44   49./44==  1.113636   98./44==  2.227273  False
i== 45   49./45==  1.088889   98./45==  2.177778  False
i== 46   49./46==  1.065217   98./46==  2.130435  False
i== 47   49./47==  1.042553   98./47==  2.085106  False
i== 48   49./48==  1.020833   98./48==  2.041667  False
i== 49   49./49==  1.000000   98./49==  2.000000  False
  - elif part is active -
  Fraction('4/8') == 1/2
  Fraction(49, 98) == 1/2
  - 4998 returned by elif section -
frac == 4998

detected :
  16/64
  19/95
  26/65
  4998

nums == ['16', '19', '26', '49']
denoms == ['64', '95', '65', '98']
pro,duct == 387296 38729600
Fraction(pro, duct) == 1/100
The elapsed time is 2.45300006866 seconds.

However the difference of formats betrays that the last value is returned by another section (elif section) than the 3 first values (returned by if section).
This deserves to be clarified.
The display shows that there is no value of i for which 49./i would be 4 and 98./i would be 8: that means that there is no reason that n[0]==4 would be a divisor of n==49 and d[1]==8 a divisor of d==98.
So the origin of the problem isn't the impossibility to represent exactly decimal values with binary representation (leading to difficulties to do comparisons in floating point arithmetic), it was a problem of algorithm.

Only if the test 49./i==1.0 and 98.i==2.00 would be performed, it would lead to the return of the value 49/98 identicaly formated as the 3 first values by the if section. So I think that the following code has the correct algorithm. But then, neither the elif section nor the else section are executed, whatever value is given to ELIF; that only means that the algorithm in the if section is equivalent to the principle in the elif section with Fraction.

def curious(numer,denom,disp=None):
    n = str(numer)
    d = str(denom)
    fracs = []
    if n!=d and n[1] == d[0]:
        ELIF = 1

        fn0 = float(n[0])
        fd1 = float(d[1])
        GCD = gcd(fn0,fd1)
        fn0DD = fn0/GCD
        fd1DD = fd1/GCD

        if disp:
            print ('---------------------------------------\n'
                   'n == %s  d == %s\n'
                   "float('%s'[0]) == %f  float('%s'[1]) == %f"
                   % ( n,d,n,fn0,d,fd1 ) )

        for i in range(2, numer+1):
            if disp:
                print ('i== %2d   '
                       '%s./%2d==%10f   %s./%2d==%10f  %s' %
                       (i,n,i,float(n)/i,d,i,float(d)/i,
                        float(n)/i==fn0DD and float(d)/i==fd1DD) )
            if float(numer)/i==fn0DD and float(denom)/i==fd1DD:
                if disp:
                    print ('  %s\n'
                           '  - %s/%s returned by if section -' %
                           ('- elif part is active -' if ELIF
                            else '- elif part is not active -',
                            n,d))
                return n + "/" + d

        if ELIF and d[1] != "0":
            if Fraction( n[0] + '/' + d[1] ) \
               == \
               Fraction(numer, denom):
                if disp:
                    print ('  %s\n'
                           "  Fraction('%s/%s') == %s\n"
                           "  Fraction(%s, %s) == %s\n"
                           '  - %s%s returned by elif section -' %
                           ('- elif part is active -' if ELIF
                            else '- elif part is not active -',
                            n[0],d[1],Fraction( n[0] + '/' + d[1] ),
                            numer,denom,Fraction(numer, denom),n,d) )
                return n + d

awaited displaying

(skipped lines)
i== 44   49./44==  1.113636   98./44==  2.227273  False
i== 45   49./45==  1.088889   98./45==  2.177778  False
i== 46   49./46==  1.065217   98./46==  2.130435  False
i== 47   49./47==  1.042553   98./47==  2.085106  False
i== 48   49./48==  1.020833   98./48==  2.041667  False
i== 49   49./49==  1.000000   98./49==  2.000000  True
  - elif part is active -
  - 49/98 returned by if section -
frac == 49/98

detected :
  16/64
  19/95
  26/65
  49/98

nums == ['16', '19', '26', '49']
denoms == ['64', '95', '65', '98']
pro,duct == 387296 38729600
Fraction(pro, duct) == 1/100
The elapsed time is 2.39100003242 seconds.

Upvotes: 2

nickie
nickie

Reputation: 5808

This is an oddly structured answer, as it was written incrementally. I apologize for this. Even so, I think it is worth reading in this order. Each part, separated with a horizontal ruler, says something different.


Equality comparisons in floating point arithmetic are usual sources of trouble and should be avoided. Instead of testing for:

float(numer)/i == float(int(n[0])) and float(denom)/i == float(int(d[1]))

why not the following? Simpler and equivalent (in mathematics, but not in Python!):

numer == i * int(n[0]) and denom == i * int(d[1])

On top of this, not only is floating point arithmetic a source of trouble but translating from int to float too often is bad for performance too.


I do not understand why you expect your program to print the same result when you remove the elif clause. It does two different things, when you have the elif and when you don't. You're just returning the same result (n/d) so you don't see the difference.

The if statement is not "being bypassed". But, if it does not return something in the first iteration of the for loop, then the elif statement (which does the same thing in every iteration) will have the chance to return its value. Then the if statement will not be executed again.


As now more information has been supplied concerning the question (this is intended as part of a solution to Euler project's problem #33), I will stick to the original version of the question and try to refactor function curious, which I believe is poorly written. I believe that the following function is equivalent and much faster, as the for loop only computes things that depend on i:

def curious(numer,denom):
    n = str(numer)
    d = str(denom)

    if len(n) == 2 == len(d) and n[1] == d[0] and n != d:
       for i in range(2, numer+1):
           if numer == i*int(n[0]) and denom == i*int(d[1]):
               return n + "/" + d
           elif i == 2 and d[1] != "0":
               if Fraction(int(n[0]), int(d[1])) == Fraction(numer, denom):
                   return n + d

With this function, Cawb07's program produces the same output (the four fractions plus 1/100, which is the answer to the problem) much faster.


Now that I understand what curious is trying to do, let me suggest a much better approach. Let us assume that a brute force solution is to be sought. It can be easily arranged so that curious is always called with two 2-digit parameters, for which numer < denom is true, and that it returns a Boolean result, denoting whether the fraction is curious or not. Furthermore, there's no need for a for loop in curious, which can be greatly simplified.

import time
from fractions import Fraction

def curious(numer, denom):
    n = str(numer)
    d = str(denom)
    return (n[0] == d[1] != '0' and denom * int(n[1]) == numer * int(d[0])) \
        or (n[0] == d[0] != '0' and denom * int(n[1]) == numer * int(d[1])) \
        or (n[1] == d[0] != '0' and denom * int(n[0]) == numer * int(d[1])) \
        or (n[1] == d[1] != '0' and denom * int(n[0]) == numer * int(d[0]))

start = time.time()

for i in range(10, 100):
    for j in range(i+1, 100):
        if curious(i, j):
           print i, "/", j

elapsed = time.time() - start
print "The elapsed time is", elapsed, "seconds."

This program just prints the four curious fractions, again much faster than the previous one.

Notice also that, for reducing the product of the curious fractions to lowest common terms, you could easily calculate the nominator and denominator and then divide by their GCD, which can be found using Euclid's algorithm.

Cawb07: Can also use fractions.gcd(n,d) :).

Upvotes: 4

Related Questions