获取python中的排列总数

R0S*_*NAM 1 python rubygems

好的,我已经编写了一个函数/方法,其中我在某个 if 条件失败后执行一些操作,但只有 1-2 次为真。

这是我的代码:

def solve_current_level(self):
    self.remaining_possibilities = list(self.remaining_possibilities)
    if len(self.remaining_possibilities) > 10 ** 6:
        #self.reduce_weapon_options()
        pass

    guess = list(random.choice(self.remaining_possibilities))
    response = self.client.solve_level(self.current_level.levelNum, guess)

    if 'roundsLeft' in response:
        self.reset_remaining_possibilities()
        return None
    elif 'response' not in response:
        return response

    self.remaining_possibilities=[possibility for possibility in self.remaining_possibilities if game.Game_evaluate(guess, list(possibility)) == response['response']]
    return None
Run Code Online (Sandbox Code Playgroud)

现在,当生成非常大的排列然后转换为列表以检查长度是否超过 10**6 时,就会出现问题,然后执行其他操作并返回。这是我目前的解决方案,但问题是当它变得非常庞大的脚本被杀死时。我从 ruby​​ 转换了这段代码,在 ruby​​ 中,可以在不转换为列表的情况下获得枚举器的大小,并且这个问题从未发生过。

这是 ruby​​ 中的代码:

def solve_current_level
  reduce_weapon_options if @remaining_possibilities.size > 10 ** 6
  if !@remaining_possibilities.kind_of?(Array)
    @remaining_possibilities = @remaining_possibilities.to_a
  end

  guess = @remaining_possibilities.sample
  response = @client.solve_level(@current_level.levelNum, guess)

  if response['roundsLeft']
    reset_remaining_possibilities
    return Nil
  elsif !response['response']
    return response
  end

  @remaining_possibilities.select! do |possibility|
    Game.evaluate(guess, possibility) == response['response']
  end
  return Nil
end
Run Code Online (Sandbox Code Playgroud)

现在你在 ruby​​ 代码中看到排列的长度在它被转换为数组/散列以继续处理之前被计算,如果数字大于 10**6 则它调用另一个方法“reduce_weapon_options”。虽然在 python 中没有办法在不转换为列表之前获得生成器的长度,但我需要它以这种方式工作,因为此时当我获得更大的范围时,它会卡住并被我的服务器杀死。我无法扩展 ram,因为我需要像 ruby​​ 一样使用更少的 ram,我绝对想避免

self.remaining_possibilities = 列表(self.remaining_possibilities)

如果条件通过/失败,则在python之前。

注意:我正在使用 itertools.permutations 来计算后来保存在“self.remaining_possibilities”中的排列

这是python和ruby中的代码:

return (0...@numWeapons).to_a.permutation(@numGladiators)
(THIS RETURNS AN ENUMERATOR OBJECT)

return it.permutations(range(0, self.numWeapons), self.numGladiators)
(THIS RETURNS A GENERATOR OBJECT)
Run Code Online (Sandbox Code Playgroud)

Joh*_*anL 5

解决这个问题的最简单方法可能是使用排列公式计算生成的排列数,可以定义为:

from math import factorial
def nPr(n, r):
    return int(factorial(n)/factorial(n-r))
Run Code Online (Sandbox Code Playgroud)

但是,这要求此数据可用或长度从创建原始排列生成器的位置传递。如果不是这种情况,出于某种原因,可以使用itertools.tee()从第一个生成器生成第二个生成器,并将其仅用于计数:

def solve_current_level(self):
    self.remaining_possibilities, perm_count = itertools.tee(self.remaining_possibilities)
    # exhausting the 'teed' generator, leaving the 'original' intact:
    num_perm = sum(1 for _ in perm_count)
    if num_perm > 10 ** 6:
        #self.reduce_weapon_options()
        pass
    # here we can still use self.remaining_possibilities
    .
    .
    .
Run Code Online (Sandbox Code Playgroud)

由于您已经在使用itertools这不是一个太重的解决方案,我猜,但它仍然需要您浏览整个列表。不过,内存占用要小得多。

  • 在 Python 3.8+ 中 `math.comb` 和 `math.perm`。 (4认同)