2013-05-04 5 views
0

У меня есть функция, которая принимает два входа и возвращает массив кортежей, где два числа в заданном кортеже имеют то же самое отношение, что и два числа, присвоенные этой функции!Почему моя функция не всегда возвращает правильный список?

Итак, все работает нормально, но по некоторым причинам в некоторых случаях он не набирает каждый кортеж. Вот пример, и я не знаю, почему:

In [52]: def find_r(num1,num2): 
    ....:   ratio = num1/float(num2) 
    ....:   ratio = 1/ratio 
    ....:   my_list = [(a,int(a * ratio)) for a in range(1,num1) if float(a * ratio).is_integer()] #and a * 1/float(ratio) + a <= num1] 
    ....:   return my_list 
    ....: 

In [53]: find_r(100,364) 
Out[53]: [(75, 273)] 

так он вернулся только один кортеж, но если вы разделите как 75 и 273 на 3, вы получите кортеж из 25 и 91, которые имеют одинаковое соотношение! Почему моя функция не подобрала этот экземпляр?

Если это помогает, я подозреваю, что это как-то связано с методом is_integer(), но я не уверен.

Спасибо!

ответ

5

Это связано с неточностью арифметики с плавающей точкой:

>>> ((100/364)*364).is_integer() 
False 
>>> ((25/91)*91).is_integer() 
False 

Вместо того, чтобы делать то, что вы делаете, вы должны проверить эквивалентность фракций путем перекрестного умножения. То есть, учитывая долю a/b, чтобы проверить, эквивалентен ли она другому c/d, проверьте, ad == bc. Это позволит избежать разделения и сохранить все как целые числа.

Вы можете сделать это:

def find_r(num1,num2): 
    return [(a, a*num2//num1) for a in range(1, num1) if (a*num2) % num1 == 0] 

>>> find_r(100, 364) 
[(25, 91), (50, 182), (75, 273)] 

(Есть и другие способы, чтобы выполнить свою задачу, но это наиболее похожий на ваш оригинальный подход.)

+0

Это имеет смысл, но, к сожалению, я не могу проверить для эквивалентности фракций, потому что я делаю список на основе элементов, имеющих точно такой же пропорции. Мне просто нужно отсеять нецелые числа, что я и делаю, но, как вы указали, некоторые из целых чисел читают как float, что является моей проблемой. –

+1

@RyanSaxe: Отношение - это доля. См. Мой отредактированный ответ для способа сделать это. – BrenBarn

0

Я думаю, что вы получите ответ, который вы ожидать

>>> r=100/float(364) 
>>> r 
0.27472527472527475 
>>> r=1/r 
>>> r 
3.6399999999999997 
>>> r*25 
90.99999999999999 
>>> r*75 
273.0 

Для того, чтобы ваши целого числа чек, вы можете использовать

как в

def find_r(num1,num2): 
     ratio = num1/float(num2) 
     ratio = 1/ratio 
     my_list = [(a,int(a * ratio)) for a in range(1,num1) if int(a * ratio) == a * ratio] 
     for a in range(1,num1): 
      if int(a * ratio) == a * ratio: 
       print a * ratio 
     return my_list 


print find_r(100,364)