【问题标题】:How to generates a list which elements are at a fix distance from a desired list如何生成一个列表,其中元素与所需列表的距离固定
【发布时间】:2018-12-10 11:05:23
【问题描述】:

我有一个可能性列表和所需的输入:

possibles = [20, 30, 40, 50, 60, 70, 80, 100]
desired = [20, 30, 40]

我想生成 close by 列表。示例:

# Distance of 1 (i.e. 1 element changes to a close-by)
[30, 30, 40]
[20, 40, 40]
[20, 30, 30]
[20, 30, 50]

# Distance of 2:
[40, 30, 40]
[30, 30, 50]
[30, 40, 40]
...

我当前的版本一次只改变一个元素,因此,一旦距离大于 1,我就会错过很多组合。

def generate_close_by(possibles, desired):
    for k in range(1, 4):
        for i, elt in enumerate(desired):
            id = possibles.index(elt)

            new = desired[:]
            if id < len(possibles)-k-1:
                new[i] = possibles[id+k]
                yield (new)

            if id > k:
                new[i] = possibles[id-k]
                yield (new)

# Output
[30, 30, 40]
[20, 40, 40]
[20, 30, 50]
[20, 30, 30]
[40, 30, 40]
[20, 50, 40]
[20, 30, 60]
[50, 30, 40]
[20, 60, 40]
[20, 30, 70]

我很确定应该已经存在一个模块来执行这种迭代(itertools?),你能指出我的 write 函数吗?

谢谢。

编辑:

尝试更新...

我正在尝试生成一个与所需大小相同的列表,其中每个元素对应于我必须移动所需元素的数量。

desired = [20, 30, 40]
# Distance of 1:
distance = [1, 0, 0]
distance = [0, 1, 0]
distance = [0, 0, 1]
distance = [-1, 0, 0]
distance = [0, -1, 0]
distance = [0, 0, -1]

然后计划尝试创建新列表,如果不能(超出范围),它会继续。还没有工作,但可能是一个好方法。

【问题讨论】:

  • 我不完全理解所需输出背后的逻辑
  • @U9-Forward 顺序无关紧要,我只写了 3 个手工制作的示例......基本上我想要 Hammnig 距离为 1 的所有组合,然后是 Hammnig 距离为 2 的所有组合,然后是 3。距离对应于:元素“移动”了多少的总和。那是我的变量k
  • desired 始终是possibles 的子集?
  • @SeljukGülcan 是的,期望总是可能的子集。尺寸 2 到 6。并且总是订购可能的。
  • 距离总是2,还是可以改变?

标签: python combinations iterable hamming-distance


【解决方案1】:

我想我会展示一个更冗长的方法,可以更容易推广。

我先把问题写下来。

possible_pts = [20, 30, 40, 50, 60, 70, 80, 100]
starting_pt_in_idx = [0, 1, 2]
distance = 2

有 3 个轴可以“改变”。我首先找到轴变化的组合。

N = len(starting_pt_in_idx)
axis = list(range(N))

import itertools
axismoves = list(itertools.combinations_with_replacement(axis, distance))
print(axismoves)

接下来,我们将其装箱。例如,如果我看到轴 0 出现了两次,它变成了 [2,0,0]。

abs_movements = []
for combi in axismoves:
    move_bin = [0] * N
    for i in combi:
        move_bin[i] += 1
    abs_movements.append(move_bin)
print(abs_movements)

上面给出了绝对动作。要找到实际的运动,我们必须考虑沿该轴的变化可能是正的或负的。

import copy
actual_movements = []
for movement in abs_movements:
    actual_movements.append(movement)
    for i, move in enumerate(movement):
        if move != 0:
            _movement = copy.deepcopy(movement)
            _movement[i] = - move
            actual_movements.append(_movement)
print(actual_movements)

最后一步是将索引转换为实际位置。所以首先我们编写这个辅助函数。

def translate_idx_to_pos(idx_vect, points):
    idx_bounds = [0, len(points) - 1]
    pos_point = [0] * len(idx_vect)
    for i, idx_pos in enumerate(idx_vect):
        if idx_pos < idx_bounds[0] or idx_pos > idx_bounds[1]:
            return None
        else:
            pos_point[i] = points[idx_pos]
    return pos_point

使用实际动作作用于起点索引,然后将其平移回位置。

from operator import add
final_pts = []
for movement in actual_movements:
    final_pt_in_idx = list(map(add, starting_pt_in_idx, movement))
    final_point = translate_idx_to_pos(final_pt_in_idx, possible_pts)
    if final_point is not None:
        final_pts.append(final_point)

print(final_pts)

这给了

[40, 30, 40]
[30, 40, 40]
[30, 20, 40]
[30, 30, 50]
[30, 30, 30]
[20, 50, 40]
[20, 40, 50]
[20, 20, 50]
[20, 40, 30]
[20, 30, 60]
[20, 30, 20]

【讨论】:

  • 我喜欢这种关于 ids 和运动的方法 :) 谢谢
  • 实际上是我想要实现的(c.f. Edit)。效果很好,速度很快!
  • 很高兴你喜欢它。而且它确实很快,因为它只列举了所需的组合并从那里构建。
  • 是的 :) 正是我想要达到的目标。我做了一些小改动,把它变成了一个生成器,它工作得很好:)
【解决方案2】:

是的,你是对的,itertools 在这里会非常有用。你想要的是找到 possibles 列表中所需长度的所有子集 WITH 重复项,并且执行此操作的函数是 itertools.product

from itertools import product

possibles = [20, 30, 40, 50, 60, 70, 80, 100]
desired = [20, 30, 40]

def fake_hamming(cur, desired, possibles):
    assert len(cur) == len(desired)

    hamm = 0
    for i in range(len(cur)):
        assert cur[i] in possibles
        assert desired[i] in possibles
        hamm += abs(possibles.index(cur[i]) - possibles.index(desired[i]))

    return hamm

def generate_close_by(desired, possibles, dist):
    all_possible_lists = product(possibles, repeat=len(desired))
    return [l for l in all_possible_lists if fake_hamming(l, desired, possibles) == dist]

print(generate_close_by(desired, possibles,1))
>>> [(20, 20, 40), (20, 30, 30), (20, 30, 50), (20, 40, 40), (30, 30, 40)]

编辑 好了,更改了产品组合(请参阅下面的 @tobias_k 评论),还有 fake_hamming 函数 xD 确实,对于大列表来说它会很慢,但这是最通用的方法

【讨论】:

  • 这就是我得到的部分,不幸的是,我在汉明距离方面遇到了一些问题。
  • 对于组合的小列表,这可能是最简单的,但对于较长的列表(比如 100 个可能性和需要 5 个元素),这很快就变得不可行了。
  • 哦,等等,我刚刚重新阅读了您的问题,这不是您想要的真正的汉明距离
  • @Zuma 也许......我不太确定它是什么^^'
  • @tobias_k possibles 非常小,
【解决方案3】:
def distribute(number, bucket):
  if bucket == 1:
    yield [number]
    if number != 0:
      yield [-1 * number]
  elif number == 0:
    yield [0]*bucket
  else:
    for i in range(number+1):
      for j in distribute(number-i, 1):
        for k in distribute(i, bucket-1):
          yield j+k

def generate(possibles, desired, distance):
  for index_distance_tuple in distribute(distance, len(desired)):
    retval = desired[:]
    for i, index in enumerate(index_distance_tuple):
      if index + i < 0 or index + i >= len(possibles):
        break
      retval[i] = possibles[index + i]
    else:
      yield retval

对于距离 1:

for i in generate(possibles, desired, 1):
  print(i)

输出:

[30, 30, 40]
[20, 40, 40]
[20, 20, 40]
[20, 30, 50]
[20, 30, 30]

对于距离 2:

for i in generate(possibles, desired, 2):
  print(i)

输出:

[40, 30, 40]
[30, 40, 40]
[30, 20, 40]
[30, 30, 50]
[30, 30, 30]
[20, 50, 40]
[20, 40, 50]
[20, 40, 30]
[20, 20, 50]
[20, 20, 30]
[20, 30, 60]
[20, 30, 20]

【讨论】:

  • 在某处有误会。 [30, 30, 40] 的距离确实为 1,因为值 20 更改为 30(1 更改为可能距离为 1 的值)。但以下距离更远。 [50, 30, 40] 在 d = 2 处,[50, 30, 40] 距离为 3..
  • 我明白了,我完全误解了。我将编辑我的答案。
  • @Mathieu,我编辑了答案。你能检查它是否是你想要的吗?
【解决方案4】:

您可以尝试递归方法:跟踪剩余距离并仅生成相关元素的组合。

def get_with_distance(poss, des, dist, k=0):
    if k < len(des):
        i = poss.index(des[k])
        for n in range(-dist, dist+1):
            if 0 <= i + n < len(poss):
                for comb in get_with_distance(poss, des, dist - abs(n), k+1):
                    yield [poss[i + n]] + comb
    elif dist == 0:
        yield []

如果仍然有 dist 剩余,这仍然会遇到一些“死胡同”,但 des 列表是空的,但总的来说,这将检查的组合比预先生成所有组合要少得多然后检查他们的距离。

如果可能的元素列表较长,您可能需要先创建一个dict 将元素映射到它们的索引,这样您就不必每次都执行poss.index(first)

例子:

possibles = [20, 30, 40, 50, 60, 70, 80, 100]
desired = [20, 30, 40]
for x in get_with_distance(possibles, desired, 2):
    print(x)

输出:

[20, 20, 30]
[20, 20, 50]
[20, 30, 20]
[20, 30, 60]
[20, 40, 30]
[20, 40, 50]
[20, 50, 40]
[30, 20, 40]
[30, 30, 30]
[30, 30, 50]
[30, 40, 40]
[40, 30, 40]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    相关资源
    最近更新 更多