- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有值的池,我想通过从某些池中选择来生成每种可能的无序组合。
例如,我想从池0,池0和池1中进行选择:
>>> pools = [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
>>> part = (0, 0, 1)
>>> list(product(*(pools[i] for i in part)))
[(1, 1, 2), (1, 1, 3), (1, 1, 4), (1, 2, 2), (1, 2, 3), (1, 2, 4), (1, 3, 2), (1, 3, 3), (1, 3, 4), (2, 1, 2), (2, 1, 3), (2, 1, 4), (2, 2, 2), (2, 2, 3), (2, 2, 4), (2, 3, 2), (2, 3, 3), (2, 3, 4), (3, 1, 2), (3, 1, 3), (3, 1, 4), (3, 2, 2), (3, 2, 3), (3, 2, 4), (3, 3, 2), (3, 3, 3), (3, 3, 4)]
(1, 2, 4)
和
(2, 1, 4)
。
combinations_with_replacement
进行排序。我计算从每个池中抽奖的次数。代码如下:
cnt = Counter()
for ind in part: cnt[ind] += 1
blocks = [combinations_with_replacement(pools[i], cnt[i]) for i in cnt]
return [list(chain(*combo)) for combo in product(*blocks)]
combinations_with_replacement
会生成一些无效的组合。有没有更有效的方法来生成无序组合?
最佳答案
这并不是一个“答案”,而是促使人们更努力地思考的问题;-)为了具体起见,我将在操作上略微打扰的OP的代码包装在一个也可以消除重复项的函数中:
def gen(pools, ixs):
from itertools import combinations_with_replacement as cwr
from itertools import chain, product
from collections import Counter
assert all(0 <= i < len(pools) for i in ixs)
seen = set()
cnt = Counter(ixs) # map index to count
blocks = [cwr(pools[i], count) for i, count in cnt.items()]
for t in product(*blocks):
t = tuple(sorted(chain(*t)))
if t not in seen:
seen.add(t)
yield t
Counter
对象所涉及的所有开销都要快。
combinations_with_replacement
(
cwr
)来获得OP的实际值(value)。考虑以下输入:
N = 64
pools = [[0, 1]]
ixs = [0] * N
pools = [[0, 1]] * N
ixs = range(N)
cwr
在这里无济于事,因为每个池索引仅出现一次。
itertools
函数的生成器),该生成器首先以字典顺序生成所有可能性(因此,从构造上讲,不会创建任何重复项)。但这首先需要对输入池进行一些“全局”分析,而且我开始编写的代码变得比现在花时间去解决的复杂得多。
cwr
和@ user2357112的增量重复数据消除,可以得到一个简短的算法,该算法可以在我拥有的所有测试用例上快速运行。例如,上面的
[0, 1] ** 64
示例的两个拼写基本上都是瞬时的,并在@Joseph Wood的答案结尾处运行该示例,其速度几乎与他所说的C++代码运行的速度相同(在Python 3.7.0下,我的代码框运行了0.35秒,并且,是的,找到162295个结果):
def gen(pools, ixs):
from itertools import combinations_with_replacement as cwr
from collections import Counter
assert all(0 <= i < len(pools) for i in ixs)
result = {()}
for i, count in Counter(ixs).items():
result = {tuple(sorted(old + new))
for new in cwr(pools[i], count)
for old in result}
return result
pools = [[1, 10, 14, 6],
[7, 2, 4, 8, 3, 11, 12],
[11, 3, 13, 4, 15, 8, 6, 5],
[10, 1, 3, 2, 9, 5, 7],
[1, 5, 10, 3, 8, 14],
[15, 3, 7, 10, 4, 5, 8, 6],
[14, 9, 11, 15],
[7, 6, 13, 14, 10, 11, 9, 4]]
ixs = range(len(pools))
T
附加到每个元组,然后再次对每个元组进行排序。然后,所导出的序列仍严格按升序排列。例如,在左列中,我们有
range(4)
中的10个唯一对,在右列中,我们向每个附加(并再次排序)2个后会发生什么:
00 002
01 012
02 022
03 023
11 112
12 122
13 123
22 222
23 223
33 233
t1
和
t2
是相邻的元组,则
t1 < t2
,并让
i
是
t1[i] != t2[i]
的最小索引。然后是
t1[i] < t2[i]
(“lexicographic <”的含义)。然后,如果将
x
放入两个元组中,按案例进行:
x <= t1[i]
吗?在
t1[i]
和
t2[i]
之间?是
x >= t2[i]
吗?在每种情况下,很容易看到第一个派生元组严格小于第二个派生元组。)
result
,当我们将新池
P
的元素添加到元组时会发生什么?好吧,如上所述
[tuple(sorted(old + (P[0],))) for old in result]
[tuple(sorted(old + (P[i],))) for old in result]
i
中的所有
range(len(P))
。可以通过
heapq.merge()
合并这些已保证已排序的序列,并在合并结果上运行另一个生成器(以下为
killdups()
)以动态清除重复项。无需保留到目前为止所看到的所有元组的集合。因为合并的输出是非递减的,所以仅检查下一个结果是否与最后一个结果输出相同就足够了。
itertools.tee()
允许下一个池的每个元素以自己的速度遍历结果-远距离序列,并在所有新池元素完成后自动释放每个结果项的内存。
build1()
(或一些类似方法)来确保在正确的时间访问正确的值。例如,如果将
build1()
的主体以内联的方式粘贴到调用的位置,则代码将严重失败(该主体将访问绑定(bind)到
rcopy
和
new
的最终值,而不是创建生成器表达式时绑定(bind)的最终值) 。
sorted()
。通过
old + new
进行附加是有原因的:
old
已经排序,并且
new
通常为1元组。在这种情况下,Python的排序是线性时间,而不是
O(N log N)
。
def gen(pools, ixs):
from itertools import combinations_with_replacement as cwr
from itertools import tee
from collections import Counter
from heapq import merge
def killdups(xs):
last = None
for x in xs:
if x != last:
yield x
last = x
def build1(rcopy, new):
return (tuple(sorted(old + new)) for old in rcopy)
assert all(0 <= i < len(pools) for i in ixs)
result = [()]
for i, count in Counter(ixs).items():
poolelts = list(cwr(pools[i], count))
xs = [build1(rcopy, new)
for rcopy, new in zip(tee(result, len(poolelts)),
poolelts)]
result = killdups(merge(*xs))
return result
x
和
y
相同,则
cwr(x, 2)
是答案。如果
x
和
y
不相交,则
product(x, y)
。否则
c
和
x
的交集
y
是非空的,答案是从3个成对不相交的集合
c
,
x-c
和
y-c
中获得的4个叉积的级联:
cwr(c, 2)
,
product(x-c, c)
,
product(y-c, c)
和
product(x-c, y-c)
。证明很简单但是很乏味,因此我将跳过它。例如,
cwr(c, 2)
和
product(x-c, c)
之间没有重复项,因为后者中的每个元组都包含
x-c
中的元素,但是前者中的每个元组仅包含
c
中的元素,并且
x-c
和
c
在构造上不相交。
product(x-c, y-c)
中没有重复项,因为两个输入是不相交的(如果它们包含一个共同的元素,那将是
x
和
y
的交集,这与
x-c
的交集中没有任何元素相矛盾)。等等。
[1, 2], [2, 3], [1, 3]
(1, 2, 3)
出现两次(分别为
(1, 2, 3)
和再次为
(2, 3, 1)
)。每对输入都有一个1元素的交集,但所有3的交集都是空的。
def pair(x, y):
from itertools import product, chain
from itertools import combinations_with_replacement
x = set(x)
y = set(y)
c = x & y
chunks = []
if c:
x -= c
y -= c
chunks.append(combinations_with_replacement(c, 2))
if x:
chunks.append(product(x, c))
if y:
chunks.append(product(y, c))
if x and y:
chunks.append(product(x, y))
return chain.from_iterable(chunks)
def matchgen(pools, ixs):
from collections import Counter
from collections import defaultdict
from itertools import chain, repeat, islice
elt2pools = defaultdict(set)
npools = 0
for i, count in Counter(ixs).items():
set_indices = set(range(npools, npools + count))
for elt in pools[i]:
elt2pools[elt] |= set_indices
npools += count
elt2count = {elt : len(ps) for elt, ps in elt2pools.items()}
cands = sorted(elt2pools.keys())
ncands = len(cands)
result = [None] * npools
# Is it possible to match result[:n] + [elt]*count?
# We already know it's possible to match result[:n], but
# this code doesn't exploit that.
def match(n, elt, count):
def extend(x, seen):
for y in elt2pools[x]:
if y not in seen:
seen.add(y)
if y in y2x:
if extend(y2x[y], seen):
y2x[y] = x
return True
else:
y2x[y] = x
return True
return False
y2x = {}
freexs = []
# A greedy pass first to grab easy matches.
for x in chain(islice(result, n), repeat(elt, count)):
for y in elt2pools[x]:
if y not in y2x:
y2x[y] = x
break
else:
freexs.append(x)
# Now do real work.
seen = set()
for x in freexs:
seen.clear()
if not extend(x, seen):
return False
return True
def inner(i, j): # fill result[j:] with elts from cands[i:]
if j >= npools:
yield tuple(result)
return
for i in range(i, ncands):
elt = cands[i]
# Find the most times `elt` can be added.
count = min(elt2count[elt], npools - j)
while count:
if match(j, elt, count):
break
count -= 1
# Since it can be added `count` times, it can also
# be added any number of times less than `count`.
for k in range(count):
result[j + k] = elt
while count:
yield from inner(i + 1, j + count)
count -= 1
return inner(0, 0)
range(10_000)
和
range(100_000)
说明。生成
(9999, 99999)
后,第一个位置增加到10000,然后持续很长时间,推断出第二个位置中10001 .. 99999中的任何可能性都不匹配;然后在第一位置的10001与第二位置的10002 .. 99999中的任何可能性都不匹配;等等。 @Leon的方案可能会注意到
range(10_000)
是唯一剩下的唯一空闲池,它在第一个位置上选择了10000,然后立即注意到
range(10_000)
不包含任何大于10000的值。显然,它需要对10001重新进行一次操作。 ,10002,...,99999在第一位置。这是线性时间而不是二次时间的浪费,但都是一样的浪费。这个故事的寓意:在您拥有实际的代码尝试之前,请不要信任任何东西;-)
match()
调用,以确定剩余的可能较小的空闲池集。也许有一种更聪明的方法可以做到这一点。
O()
最坏情况行为。以我的经验,在典型情况下,简单的深度优先(如此处)搜索在现实生活中运行得更快。但这在很大程度上取决于当前应用程序中“典型”图形的特征。我没有在这里尝试其他方法。
def matchgen(pools, ixs):
from collections import Counter
from collections import defaultdict
from itertools import islice
elt2pools = defaultdict(list)
allpools = []
npools = 0
for i, count in Counter(ixs).items():
indices = list(range(npools, npools + count))
plist = sorted(pools[i])
for elt in plist:
elt2pools[elt].extend(indices)
for i in range(count):
allpools.append(plist)
npools += count
pools = allpools
assert npools == len(pools)
result = [None] * npools
# Is it possible to match result[:n] not using pool
# bady? If not, return None. Else return a matching,
# a dict whose keys are pool indices and whose values
# are a permutation of result[:n].
def match(n, bady):
def extend(x, seen):
for y in elt2pools[x]:
if y not in seen:
seen.add(y)
if y not in y2x or extend(y2x[y], seen):
y2x[y] = x
return True
return False
y2x = {}
freexs = []
# A greedy pass first to grab easy matches.
for x in islice(result, n):
for y in elt2pools[x]:
if y not in y2x and y != bady:
y2x[y] = x
break
else:
freexs.append(x)
# Now do real work.
for x in freexs:
if not extend(x, {bady}):
return None
return y2x
def inner(j, freepools): # fill result[j:]
from bisect import bisect_left
if j >= npools:
yield tuple(result)
return
if j:
new_freepools = set()
allcands = set()
exhausted = set() # free pools with elts too small
atleast = result[j-1]
for pi in freepools:
if pi not in new_freepools:
m = match(j, pi)
if not m: # match must use pi
continue
# Since `m` is a match to result[:j],
# any pool in freepools it does _not_
# use must still be free.
new_freepools |= freepools - m.keys()
assert pi in new_freepools
# pi is free with respect to result[:j].
pool = pools[pi]
if pool[-1] < atleast:
exhausted.add(pi)
else:
i = bisect_left(pool, atleast)
allcands.update(pool[i:])
if exhausted:
freepools -= exhausted
new_freepools -= exhausted
else: # j == 0
new_freepools = freepools
allcands = elt2pools.keys()
for result[j] in sorted(allcands):
yield from inner(j + 1, new_freepools)
return inner(0, set(range(npools)))
[0, 1]
会花费大约2分钟(!)的时间来查找129个结果。 POC代码花了不到一秒钟的时间,而某些不匹配的方法则是瞬间出现的。
match()
从来没有遇到困难,总是会在第一次(贪婪)遍历中找到前缀的完全匹配,但是即使这样,时间也与当前前缀长度的平方成正比(==当前递归深度)。
[0, 1]
池的129个结果在不到十分之一秒而不是两分钟的时间内交付。事实证明,在这种情况下,不需要进行图匹配。
def combogen(pools, ixs):
from collections import Counter
from collections import defaultdict
from itertools import islice
elt2pools = defaultdict(set)
npools = 0
cands = []
MAXTUPLE = []
for i, count in Counter(ixs).items():
indices = set(range(npools, npools + count))
huge = None
for elt in pools[i]:
elt2pools[elt] |= indices
for i in range(count):
cands.append(elt)
if huge is None or elt > huge:
huge = elt
MAXTUPLE.extend([huge] * count)
npools += count
MAXTUPLE = tuple(sorted(MAXTUPLE))
cands.sort()
ncands = len(cands)
ALLPOOLS = set(range(npools))
availpools = ALLPOOLS.copy()
result = [None] * npools
class Finished(Exception):
pass
# Is it possible to match result[:n]? If not, return None. Else
# return a matching, a dict whose keys are pool indices and
# whose values are a permutation of result[:n].
def match(n):
def extend(x, seen):
for y in elt2pools[x]:
if y not in seen:
seen.add(y)
if y not in y2x or extend(y2x[y], seen):
y2x[y] = x
return True
return False
y2x = {}
freexs = []
# A greedy pass first to grab easy matches.
for x in islice(result, n):
for y in elt2pools[x]:
if y not in y2x:
y2x[y] = x
break
else:
freexs.append(x)
# Now do real work.
seen = set()
for x in freexs:
seen.clear()
if not extend(x, seen):
return None
return y2x
def inner(i, j): # fill result[j:] with cands[i:]
nonlocal availpools
if j >= npools:
r = tuple(result)
yield r
if r == MAXTUPLE:
raise Finished
return
restore_availpools = None
last = None
jp1 = j + 1
for i in range(i, ncands):
elt = cands[i]
if elt == last:
continue
last = result[j] = elt
pools = elt2pools[elt] & availpools
if pools:
pool = pools.pop() # pick one - arbitrary
availpools.remove(pool)
else:
# Find _a_ matching, and if that's possible fiddle
# availpools to pretend that's the one we used all
# along.
m = match(jp1)
if not m: # the prefix can't be extended with elt
continue
if restore_availpools is None:
restore_availpools = availpools.copy()
availpools = ALLPOOLS - m.keys()
# Find a pool from which elt was taken.
for pool, v in m.items():
if v == elt:
break
else:
assert False
yield from inner(i+1, jp1)
availpools.add(pool)
if restore_availpools is not None:
availpools = restore_availpools
try:
yield from inner(0, 0)
except Finished:
pass
关于python - 从重叠的池中挑选无序组合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51834467/
我正在处理一组标记为 160 个组的 173k 点。我想通过合并最接近的(到 9 或 10 个组)来减少组/集群的数量。我搜索过 sklearn 或类似的库,但没有成功。 我猜它只是通过 knn 聚类
我有一个扁平数字列表,这些数字逻辑上以 3 为一组,其中每个三元组是 (number, __ignored, flag[0 or 1]),例如: [7,56,1, 8,0,0, 2,0,0, 6,1,
我正在使用 pipenv 来管理我的包。我想编写一个 python 脚本来调用另一个使用不同虚拟环境(VE)的 python 脚本。 如何运行使用 VE1 的 python 脚本 1 并调用另一个 p
假设我有一个文件 script.py 位于 path = "foo/bar/script.py"。我正在寻找一种在 Python 中通过函数 execute_script() 从我的主要 Python
这听起来像是谜语或笑话,但实际上我还没有找到这个问题的答案。 问题到底是什么? 我想运行 2 个脚本。在第一个脚本中,我调用另一个脚本,但我希望它们继续并行,而不是在两个单独的线程中。主要是我不希望第
我有一个带有 python 2.5.5 的软件。我想发送一个命令,该命令将在 python 2.7.5 中启动一个脚本,然后继续执行该脚本。 我试过用 #!python2.7.5 和http://re
我在 python 命令行(使用 python 2.7)中,并尝试运行 Python 脚本。我的操作系统是 Windows 7。我已将我的目录设置为包含我所有脚本的文件夹,使用: os.chdir("
剧透:部分解决(见最后)。 以下是使用 Python 嵌入的代码示例: #include int main(int argc, char** argv) { Py_SetPythonHome
假设我有以下列表,对应于及时的股票价格: prices = [1, 3, 7, 10, 9, 8, 5, 3, 6, 8, 12, 9, 6, 10, 13, 8, 4, 11] 我想确定以下总体上最
所以我试图在选择某个单选按钮时更改此框架的背景。 我的框架位于一个类中,并且单选按钮的功能位于该类之外。 (这样我就可以在所有其他框架上调用它们。) 问题是每当我选择单选按钮时都会出现以下错误: co
我正在尝试将字符串与 python 中的正则表达式进行比较,如下所示, #!/usr/bin/env python3 import re str1 = "Expecting property name
考虑以下原型(prototype) Boost.Python 模块,该模块从单独的 C++ 头文件中引入类“D”。 /* file: a/b.cpp */ BOOST_PYTHON_MODULE(c)
如何编写一个程序来“识别函数调用的行号?” python 检查模块提供了定位行号的选项,但是, def di(): return inspect.currentframe().f_back.f_l
我已经使用 macports 安装了 Python 2.7,并且由于我的 $PATH 变量,这就是我输入 $ python 时得到的变量。然而,virtualenv 默认使用 Python 2.6,除
我只想问如何加快 python 上的 re.search 速度。 我有一个很长的字符串行,长度为 176861(即带有一些符号的字母数字字符),我使用此函数测试了该行以进行研究: def getExe
list1= [u'%app%%General%%Council%', u'%people%', u'%people%%Regional%%Council%%Mandate%', u'%ppp%%Ge
这个问题在这里已经有了答案: Is it Pythonic to use list comprehensions for just side effects? (7 个答案) 关闭 4 个月前。 告
我想用 Python 将两个列表组合成一个列表,方法如下: a = [1,1,1,2,2,2,3,3,3,3] b= ["Sun", "is", "bright", "June","and" ,"Ju
我正在运行带有最新 Boost 发行版 (1.55.0) 的 Mac OS X 10.8.4 (Darwin 12.4.0)。我正在按照说明 here构建包含在我的发行版中的教程 Boost-Pyth
学习 Python,我正在尝试制作一个没有任何第 3 方库的网络抓取工具,这样过程对我来说并没有简化,而且我知道我在做什么。我浏览了一些在线资源,但所有这些都让我对某些事情感到困惑。 html 看起来
我是一名优秀的程序员,十分优秀!