Python 代码占用了太多的时间
在接受用户输入后,我编写了排列数字的代码。排序要求相邻数之和为素数。直到10作为输入代码工作正常。如果超出这个范围,系统将挂起。请让我知道优化它的步骤 ex输入8Python 代码占用了太多的时间,python,Python,在接受用户输入后,我编写了排列数字的代码。排序要求相邻数之和为素数。直到10作为输入代码工作正常。如果超出这个范围,系统将挂起。请让我知道优化它的步骤 ex输入8 答案应该是:(1,2,3,4,7,6,5,8) 代码如下 import itertools x = raw_input("please enter a number") range_x = range(int(x)+1) del range_x[0] result = list(itertools.permutations(rang
答案应该是:(1,2,3,4,7,6,5,8)
代码如下
import itertools
x = raw_input("please enter a number")
range_x = range(int(x)+1)
del range_x[0]
result = list(itertools.permutations(range_x))
def prime(x):
for i in xrange(1,x,2):
if i == 1:
i = i+1
if x%i==0 and i < x :
return False
else:
return True
def is_prime(a):
for i in xrange(len(a)):
print a
if i < len(a)-1:
if prime(a[i]+a[i+1]):
pass
else:
return False
else:
return True
for i in xrange(len(result)):
if i < len(result)-1:
if is_prime(result[i]):
print 'result is:'
print result[i]
break
else:
print 'result is'
print result[i-1]
导入itertools
x=原始输入(“请输入一个数字”)
range_x=range(int(x)+1)
del range_x[0]
结果=列表(itertools.排列(范围x))
def基本(x):
对于x范围内的i(1,x,2):
如果i==1:
i=i+1
如果x%i==0且i
动态规划,拯救:
def is_prime(n):
return all(n % i != 0 for i in range(2, n))
def order(numbers, current=[]):
if not numbers:
return current
for i, n in enumerate(numbers):
if current and not is_prime(n + current[-1]):
continue
result = order(numbers[:i] + numbers[i + 1:], current + [n])
if result:
return result
return False
result = order(range(500))
for i in range(len(result) - 1):
assert is_prime(result[i] + result[i + 1])
您可以通过增加最大递归深度来强制它在更大的列表中工作。以下是我对解决方案的看法。正如蒂姆·彼得斯指出的,这是一个哈密顿路径问题。 因此,第一步是以某种形式生成图形 这是生成素数的第0步。我要用筛子,但不管什么基本测试都可以。我们需要素数高达
2*n
,因为这是任意两个数字之和的最大值
m = 8
n = m + 1 # Just so I don't have to worry about zero indexes and random +/- 1's
primelen = 2 * m
prime = [True] * primelen
prime[0] = prime[1] = False
for i in range(4, primelen, 2):
prime[i] = False
for i in range(3, primelen, 2):
if not prime[i]:
continue
for j in range(i * i, primelen, i):
prime[j] = False
好的,现在我们可以用prime[i]
测试素性。现在它很容易使图形的边缘。如果我有一个数字I,接下来会出现什么数字。我还将利用I和j具有相反的奇偶性这一事实
pairs = [set(j for j in range(i%2+1, n, 2) if prime[i+j])
for i in range(n)]
所以这里,pairs[i]
是一个集合对象,它的元素是整数j,使得i+j
是素数
现在我们需要沿着图表走。这确实是耗时的部分,所有进一步的优化都将在这里完成
chains = [
([], set(range(1, n))
]
链
将在我们行走时跟踪有效路径。元组中的第一个元素将是结果。第二个元素是所有未使用的编号或未访问的节点。这个想法是从队列中取出一条链,沿着路径走一步,然后把它放回去
while chains:
chain, unused = chains.pop()
if not chain:
# we haven't even started, all unused are valid
valid_next = unused
else:
# We need numbers that are both unused and paired with the last node
# Using sets makes this easy
valid_next = unused & pairs[chains[-1]]
for num in valid_next:
# Take a step to the new node and add the new path back to chains
# Reminder, its important not to mutate anything here, always make new objs
newchain = chain + [num]
newunused = unused - set([num])
chains.append( (newchain, newunused) )
# are we done?
if not newunused:
print newchain
chains = False
请注意,如果没有有效的下一步,将删除该路径而不进行替换
这确实是内存效率低下,但运行时间合理。最大的性能瓶颈是遍历图表,因此下一个优化将是在智能位置弹出并插入路径,以对最可能的路径进行优先级排序。在这种情况下,为链使用collections.deque
或其他容器可能会有所帮助
编辑
下面是一个如何实现路径优先级的示例。我们将为每个路径分配一个分数,并按此分数对链
列表进行排序。举一个简单的例子,我建议包含“更难使用”节点的路径更有价值。也就是说,对于路径上的每一步,分数将增加n-len(valid\u next)
修改后的代码将如下所示
import bisect
chains = ...
chains_score = [0]
while chains:
chain, unused = chains.pop()
score = chains_score.pop()
...
for num in valid_next:
newchain = chain + [num]
newunused = unused - set([num])
newscore = score + n - len(valid_next)
index = bisect.bisect(chains_score, newscore)
chains.insert(index, (newchain, newunused))
chains_score.insert(index, newscore)
请记住,插入是O(n)
,因此添加此项的开销可能相当大。对分数算法进行一些分析以保持队列长度len(chains)
可管理是值得的。这个答案是基于
有许多可能的解决办法。为了避免中间解决方案的过度内存消耗,可以生成随机路径。它还允许轻松利用多个cpu(每个cpu并行生成自己的路径)
在哪里:
以及:
例子
输出
输出为满足以下条件的随机序列:
- 它是范围为1到20(包括)的排列
- 相邻数之和为素数
n=900
的结果平均需要10秒左右,将时间外推为指数函数,得到n=1000
的结果应该需要20秒左右:
图像是使用以下代码生成的:
import numpy as np
figname = 'hamiltonian_random_noset-noseq-900-900'
Ns, Ts = np.loadtxt(figname+'.xy', unpack=True)
# use polyfit to fit the data
# y = c*a**n
# log y = log (c * a ** n)
# log Ts = log c + Ns * log a
coeffs = np.polyfit(Ns, np.log2(Ts), deg=1)
poly = np.poly1d(coeffs, variable='Ns')
# use curve_fit to fit the data
from scipy.optimize import curve_fit
def func(x, a, c):
return c*a**x
popt, pcov = curve_fit(func, Ns, Ts)
aa, cc = popt
a, c = 2**coeffs
# plot it
import matplotlib.pyplot as plt
plt.figure()
plt.plot(Ns, np.log2(Ts), 'ko', label='time measurements')
plt.plot(Ns, np.polyval(poly, Ns), 'r-',
label=r'$time = %.2g\times %.4g^N$' % (c, a))
plt.plot(Ns, np.log2(func(Ns, *popt)), 'b-',
label=r'$time = %.2g\times %.4g^N$' % (cc, aa))
plt.xlabel('N')
plt.ylabel('log2(time in seconds)')
plt.legend(loc='upper left')
plt.show()
拟合值:
>>> c*a**np.array([900, 1000])
array([ 11.37200806, 21.56029156])
>>> func([900, 1000], *popt)
array([ 14.1521409 , 22.62916398])
为子孙后代;-),这里还有一个是基于找到哈密顿路径的。这是Python3代码。如前所述,它在找到第一条路径时停止,但可以很容易地更改以生成所有路径。在我的框中,它找到了1到900中所有n
的解决方案,总共大约一分钟。对于略大于900的n
,它超过了最大递归深度
prime生成器(psieve()
)对于这个特定问题来说是一个非常复杂的工具,但我手头有它,不想再写一个;-)
路径查找器(ham()
)是一种递归回溯搜索,通常(但不总是)使用一种非常有效的排序启发式方法:在迄今为止与路径中最后一个顶点相邻的所有顶点中,首先查看剩余出口最少的顶点。例如,这是用于解决骑士巡演问题的“常用”启发式方法。在这种情况下,它通常会找到一个完全不需要回溯的旅行。你的问题似乎比那更棘手
def psieve():
import itertools
yield from (2, 3, 5, 7)
D = {}
ps = psieve()
next(ps)
p = next(ps)
assert p == 3
psq = p*p
for i in itertools.count(9, 2):
if i in D: # composite
step = D.pop(i)
elif i < psq: # prime
yield i
continue
else: # composite, = p*p
assert i == psq
step = 2*p
p = next(ps)
psq = p*p
i += step
while i in D:
i += step
D[i] = step
def build_graph(n):
primes = set()
for p in psieve():
if p > 2*n:
break
else:
primes.add(p)
np1 = n+1
adj = [set() for i in range(np1)]
for i in range(1, np1):
for j in range(i+1, np1):
if i+j in primes:
adj[i].add(j)
adj[j].add(i)
return set(range(1, np1)), adj
def ham(nodes, adj):
class EarlyExit(Exception):
pass
def inner(index):
if index == n:
raise EarlyExit
avail = adj[result[index-1]] if index else nodes
for i in sorted(avail, key=lambda j: len(adj[j])):
# Remove vertex i from the graph. If this isolates
# more than 1 vertex, no path is possible.
result[index] = i
nodes.remove(i)
nisolated = 0
for j in adj[i]:
adj[j].remove(i)
if not adj[j]:
nisolated += 1
if nisolated > 1:
break
if nisolated < 2:
inner(index + 1)
nodes.add(i)
for j in adj[i]:
adj[j].add(i)
n = len(nodes)
result = [None] * n
try:
inner(0)
except EarlyExit:
return result
def solve(n):
nodes, adj = build_graph(n)
return ham(nodes, adj)
def psieve():
进口itertools
(2,3,5,7)的收益率
D={}
ps=psieve()
下一页(ps)
p=下一个(ps)
断言p==3
psq=p*p
对于itertools中的i.count(9,2):
如果我在D:#复合
步骤=D.pop(i)
elif i$ python order-adjacent-prime-sum.py 20
[19, 18, 13, 10, 1, 4, 9, 14, 5, 6, 17, 2, 15, 16, 7, 12, 11, 8, 3, 20]
import numpy as np
figname = 'hamiltonian_random_noset-noseq-900-900'
Ns, Ts = np.loadtxt(figname+'.xy', unpack=True)
# use polyfit to fit the data
# y = c*a**n
# log y = log (c * a ** n)
# log Ts = log c + Ns * log a
coeffs = np.polyfit(Ns, np.log2(Ts), deg=1)
poly = np.poly1d(coeffs, variable='Ns')
# use curve_fit to fit the data
from scipy.optimize import curve_fit
def func(x, a, c):
return c*a**x
popt, pcov = curve_fit(func, Ns, Ts)
aa, cc = popt
a, c = 2**coeffs
# plot it
import matplotlib.pyplot as plt
plt.figure()
plt.plot(Ns, np.log2(Ts), 'ko', label='time measurements')
plt.plot(Ns, np.polyval(poly, Ns), 'r-',
label=r'$time = %.2g\times %.4g^N$' % (c, a))
plt.plot(Ns, np.log2(func(Ns, *popt)), 'b-',
label=r'$time = %.2g\times %.4g^N$' % (cc, aa))
plt.xlabel('N')
plt.ylabel('log2(time in seconds)')
plt.legend(loc='upper left')
plt.show()
>>> c*a**np.array([900, 1000])
array([ 11.37200806, 21.56029156])
>>> func([900, 1000], *popt)
array([ 14.1521409 , 22.62916398])
def psieve():
import itertools
yield from (2, 3, 5, 7)
D = {}
ps = psieve()
next(ps)
p = next(ps)
assert p == 3
psq = p*p
for i in itertools.count(9, 2):
if i in D: # composite
step = D.pop(i)
elif i < psq: # prime
yield i
continue
else: # composite, = p*p
assert i == psq
step = 2*p
p = next(ps)
psq = p*p
i += step
while i in D:
i += step
D[i] = step
def build_graph(n):
primes = set()
for p in psieve():
if p > 2*n:
break
else:
primes.add(p)
np1 = n+1
adj = [set() for i in range(np1)]
for i in range(1, np1):
for j in range(i+1, np1):
if i+j in primes:
adj[i].add(j)
adj[j].add(i)
return set(range(1, np1)), adj
def ham(nodes, adj):
class EarlyExit(Exception):
pass
def inner(index):
if index == n:
raise EarlyExit
avail = adj[result[index-1]] if index else nodes
for i in sorted(avail, key=lambda j: len(adj[j])):
# Remove vertex i from the graph. If this isolates
# more than 1 vertex, no path is possible.
result[index] = i
nodes.remove(i)
nisolated = 0
for j in adj[i]:
adj[j].remove(i)
if not adj[j]:
nisolated += 1
if nisolated > 1:
break
if nisolated < 2:
inner(index + 1)
nodes.add(i)
for j in adj[i]:
adj[j].add(i)
n = len(nodes)
result = [None] * n
try:
inner(0)
except EarlyExit:
return result
def solve(n):
nodes, adj = build_graph(n)
return ham(nodes, adj)