Python 从列表中获取分数近似值
我刚刚开始学习Python 这是一个齿轮率计算器 我有一个5000个整数的列表,范围从367到7645373,还有一个原始分数,它可以是1/10或34561/43521到10/1 我需要制作一个新的分数,它的值和原始分数一样接近,由表中的分子和分母组成 事实上,我想要一个匹配列表,按照它与原始分数的偏差排序 我有一个解决方案,但给出值为1/10的结果需要时间,因为像367/3670或368/3680或4352/43520…这样的解决方案是等效的 Pythonist会怎么做 请不要告诉我这是一个C库的案例D 干杯Python 从列表中获取分数近似值,python,list,matching,Python,List,Matching,我刚刚开始学习Python 这是一个齿轮率计算器 我有一个5000个整数的列表,范围从367到7645373,还有一个原始分数,它可以是1/10或34561/43521到10/1 我需要制作一个新的分数,它的值和原始分数一样接近,由表中的分子和分母组成 事实上,我想要一个匹配列表,按照它与原始分数的偏差排序 我有一个解决方案,但给出值为1/10的结果需要时间,因为像367/3670或368/3680或4352/43520…这样的解决方案是等效的 Pythonist会怎么做 请不要告诉我这是一个C
安东尼奥
计算所有结果,然后进行排序
from collections import namedtuple
import random
fraction_target = 1 / 10.0
limit_left = fraction_target * 0.9
limit_right = fraction_target * 1.1
result = namedtuple('result', 'val1 val2 fraction')
values = [random.randint(367, 7645373) for _ in range(5000)]
fraction_results = []
for value_1 in values:
for value_2 in values:
fraction = value_1 / float(value_2)
if limit_left < fraction < limit_right:
fraction_results.append(result(value_1, value_2, fraction))
fraction_results.sort(key=lambda x: abs(x.fraction - fraction_target))
print(fraction_results[0:10])
从集合导入namedtuple
随机输入
分数\目标=1/10.0
左极限=分数目标*0.9
限制右=分数目标*1.1
结果=namedtuple('result','val1 val2分数')
值=[random.randint(3677645373)表示范围内(5000)]
分数_结果=[]
对于值中的值_1:
对于值中的值_2:
分数=值_1/浮点数(值_2)
如果左极限<分数<右极限:
分数\结果。追加(结果(值\ 1,值\ 2,分数))
分数结果排序(key=lambda x:abs(x.fraction-fraction\u target))
打印(分数_结果[0:10])
我将存储的结果限制在所需分数的90%到110%之间;使用NumPy的矢量化解决方案可能更快
def search_ratio(l, a):
l = np.array(l)
t = l.astype(float).reshape(-1, 1) / l.reshape(1, -1)
i = np.unravel_index(np.argsort(np.where(t > a, t / a, a / t).flat), t.shape)
return l[i[0]], l[i[1]]
例如,search\u ratio(范围(2,6),1.3)
将给出:
(array([4, 5, 3, 5, 2, 3, 4, 5, 4, 4, 3, 5, 2, 3, 2, 2]),
array([3, 4, 2, 3, 2, 3, 4, 5, 2, 5, 4, 2, 3, 5, 4, 5]))
由于4/3
是与1.3最接近的比率,5/4
是次接近的比率,以此类推
注意,t
,可用比率表,可以缓存以提高效率
from __future__ import division
import itertools
def searchratio(input_range):
my_ratios=set()
for x in itertools.combinations(input_range, 2):
y=x[0]/x[1]
if y==1/10 or (10/1>y>34561/43521):
my_ratios.add(x)
return my_ratios
if __name__=='__main__':
from time import time
t1=time()
nk=len(searchratio(xrange(4000)))
t2=time()
print t2-t1, nk
在python中;查看4000
项目列表需要6.5秒在pypy;查看
4000
项目列表需要3.25秒如果你想进一步降低它;您必须选择
cython
或将代码放入ipython
的并行处理中。
简单的步骤就是在
pypy-c
(https://pypy.org/
);您可以立即看到时间减少了50%由于此解决方案的局限性,我不确定这是否会有帮助,但我还是发布了它,因为该理论至少非常适用于您的问题
我假设你的列表只是一个数字列表。因此,让我们首先将其设置为一个集合
,因为我们会反复检查成员资格,使用集合会更快:
integers = set(integers)
现在,fracts.fracts
类有一个漂亮的方法,名为,它可能会对您有所帮助。它基于,可以用来构造任何实数的a。然后,可以使用一个数的连分数构造该数在给定分母约束下的最佳有理逼近
因此,为了将它应用到你的问题中,我们从你想要表示的分数开始,用一个低于先前近似分母的极限反复调用极限分母,直到我们找到一个分子和分母都在整数中的分数。因为更大的分母意味着更好的近似,所以第一个匹配是最好的
import fractions
def best_approximation(numerator, denominator, integers):
min_int = min(integers)
max_int = max(integers)
org_frac = fractions.Fraction(numerator, denominator)
if org_frac > 1:
# the numerator is larger than the denominator, so our maximum
# denominator must be adjusted down accordingly, since we also
# want the numerator to be below max_int
curr_max = int(max_int / org_frac)
else:
curr_max = max_int
while True:
fr = org_frac.limit_denominator(max_int)
if fr.numerator < min_int or fr.denominator < min_int:
return None
if fr.numerator in integers and fr.denominator in integers:
return fr
max_int = fr.denominator - 1
由于limit\u分母
方法构建的内部结构可以重用,因此这仍然没有可能的快。您可以通过复制源代码并对其进行修改来解决这个问题,或者使用wikipedia文章从头开始实现算法
我提到的局限性是:使用这种方法可能找不到最佳近似值。如果整数列表过于稀疏,这可能是一个问题。在我的素数列表的具体例子中,使用从7645373到17645373的随机分子和分母,它只适用于大约一半的情况,这显然不够好。另一方面,当你得到一个答案时,你会知道这是一个非常好的近似值。我最后使用了带排序列表的对分
import bisect
import random
import time
def searchratio(t,n,d):
#n must be <= d if it's not he case swap ,and swap the results returned
b=[]
a=float(n)/d
t0=t[0]
x1=bisect.bisect(t,t0/a)
print a,x1,t0
lm=len(t)-2
for ti in t[x1:lm]:
x1=bisect.bisect_left(t,ti*a)
b.append([t[x1],ti])
b.append([t[x1+1],ti])
b.sort(key = lambda x:abs(x[0]/float(x[1])-a))
return b
#-----test program--------------------------------------------------
l=[random.randrange(100000,10000000) for i in xrange(100000)]
l.sort()
n=3
d=100
t=time.clock()
r=searchratio(l,n,d)
t=time.clock() - t
print t, len(r), float(n)/d
for x in r:
print x, x[0]/float(x[1])
导入对分
随机输入
导入时间
def搜索比率(t、n、d):
#n必须为,因此您希望列出那些分数与给定范围/值列表中的分数相同的(分子、分母)
,它们与原始分数有多接近?需要知道如何缩小2500万个可能的分数,所有这些分数都会在一定程度上偏离。如果你将数据分类到BST,然后从每个节点进行遍历,我认为这将帮助你更快地缩小最佳候选分数的范围。所以你说这是你想要的,但速度太慢了,对吗?传递给函数的l
参数的内容和格式是什么?它不是一个简单的“范围从367到7645373的5000个整数的列表”。分数是标准模块的名称。这将比它需要的慢。这仅仅是因为你在一个速度非常慢的列表上调用append。对于你的输入列表
=范围(4000)在我的笔记本电脑上花了79秒我不是在谈论你的输入列表。在my_final_list
上调用append
,就是我所引用的。您还看到在对象上调用append
方法的其他地方吗?这就是为什么我写下我刚刚找到的答案;79秒。有没有别的办法可以用append来加速呢?啊,我以为你的意思是调用range(4000)
需要79秒(听起来不太对劲,但我听到过更多奇怪的事情)。一种更快的方法是列表理解,但随后您就不再需要先检查集合。我想在这种情况下没有更好的办法了。
>>> print best_approximation(34561, 43521, integers)
4513/5683
import bisect
import random
import time
def searchratio(t,n,d):
#n must be <= d if it's not he case swap ,and swap the results returned
b=[]
a=float(n)/d
t0=t[0]
x1=bisect.bisect(t,t0/a)
print a,x1,t0
lm=len(t)-2
for ti in t[x1:lm]:
x1=bisect.bisect_left(t,ti*a)
b.append([t[x1],ti])
b.append([t[x1+1],ti])
b.sort(key = lambda x:abs(x[0]/float(x[1])-a))
return b
#-----test program--------------------------------------------------
l=[random.randrange(100000,10000000) for i in xrange(100000)]
l.sort()
n=3
d=100
t=time.clock()
r=searchratio(l,n,d)
t=time.clock() - t
print t, len(r), float(n)/d
for x in r:
print x, x[0]/float(x[1])