【问题标题】:Find all combinations of 3x3 holepunch查找 3x3 打孔的所有组合
【发布时间】:2011-11-30 23:48:11
【问题描述】:

我参加了一个狂欢节,他们在每个地点都用特殊的打孔器标记您的程序。打孔器是一个 3x3 空间的网格。在每个空间中,要么有一根针刺穿你的纸,要么没有。这让我想知道你可以用这个工具制作多少种不同的图案。我的第一个想法是:2^9 = 512,但所有 9 个空格都没有针脚并不是真正的一拳,所以真的是:511。

然后复杂性打击了我。特别是因为工人在给你的纸打孔时并不那么小心,所以这些看起来都是一样的:

x..  .x.  ...  etc.
.x.  x..  .x.
...  ...  ..x

问题:如何编写测试来说明轮换和移位?


到目前为止的勤奋和想法:

  • 二进制感觉像是这个等式的一个明显部分
  • 找到独特的模式后,将其存储在内存中,以便可以针对它测试未来的模式
  • 有 4 种旋转可能性。
    编辑:我所说的“旋转”是指您可以采用任何形状并将其旋转 90 度。考虑左上角的点图案。您可以将其旋转 90 度并获得右上角的点。再做一次,它在右下角。再次,它在左下角。使用纯 2^9 计算,这是 4 种不同的组合。然而,对于这个问题,这些正是我要清除的重复项。
  • 对于每次旋转,有 25 种方法可以使 3x3 网格重叠:

重叠:

/ = the spaces in the new one to test
\ = the spaces in a verified unique one

1               2               25
/ / / . . . .   . / / / . . .   . . . . . . .
/ / / . . . .   . / / / . . .   . . . . . . .
/ / X \ \ . .   . / X X \ . .   . . \ \ \ . .
. . \ \ \ . .   . . \ \ \ . .   . . \ \ \ . .
. . \ \ \ . .   . . \ \ \ . .   . . \ \ X / /
. . . . . . .   . . . . . . .   . . . . / / /
. . . . . . .   . . . . . . .   . . . . / / /
  • 如果任一模式包含不在重叠区域中的引脚,则无需测试重叠。按位 AND 可以在这里提供帮助。
  • 如果将 2 个模式的每个位置都设置为字符串,则只需检查是否相等
  • 能否将前两种想法结合起来提高效率?

【问题讨论】:

  • +1 有趣的问题。
  • 有一种方法可以计算所有这些可能性,但我现在忘记了。如果您真的对这个概念感兴趣,请阅读本书的第 5 章,它有一个与您的问题完全相同的示例(或在 math.stackexchange.com 上提问):books.google.com/…
  • 盲文字母表由类似的约束构成。
  • 这些冲头也需要在冲头翻转时产生的反射是唯一的。因此,需要排除已包含的左/右或上/下反射的任何模式。
  • @Dinah,您希望看到问题的任何具体方面吗?

标签: algorithm combinations


【解决方案1】:

我们只需要考虑在第一行和第一列有打孔的图案。如果第一行是空的,模式可以向上移动。如果第一列为空,则模式可以向左移动。无论哪种情况,我们都可以得出我们确实考虑过的类似模式。

对于这些模式,我们需要检查旋转的版本是否相同。为此,我们最多应用三个 90 度旋转,可能向左移动以删除前导空列(第一行永远不会为空)并找到具有最低数值的模式。

然后我们可以将此值添加到哈希集中,该哈希集将只保留唯一值。

不包括空模式,因为它的所有行都是空的。

为了实现这一点,我们将模式编码为连续的位:

012
345
678

我们需要的操作大多非常简单:

Test for an empty row:    (n & 7) == 0     // bits 0,1,2 not set
Test for an empty column: (n & 73) == 0    // bits 0,3,6 not set
Shift pattern up:         n -> (n >> 3)
Shift pattern left:       n -> (n >> 1)

最棘手的部分是旋转,实际上只是重新排列所有位:

n -> ((n & 1) << 2) + ((n & 2) << 4) + ((n & 4) << 6)
   + ((n & 8) >> 2) + (n & 16) + ((n & 32) << 2)
   + ((n & 64) >> 6) + ((n & 128) >> 4) + ((n & 256) >> 2);

在 C# 中:

public static int Count3x3() {
    HashSet<int> patterns = new HashSet<int>();
    for (int i = 0; i < 512; i++) {
        if ((i & 7) == 0 || (i & 73) == 0)
            continue;
        int nLowest = i;
        int n = i;
        do {
            nLowest = Math.Min(nLowest, n);
            n = ((n & 1) << 2) + ((n & 2) << 4) + ((n & 4) << 6)
                + ((n & 8) >> 2) + (n & 16) + ((n & 32) << 2)
                + ((n & 64) >> 6) + ((n & 128) >> 4) + ((n & 256) >> 2);
            while ((n & 73) == 0)
                n >>= 1;
        } while (n != i);
        patterns.Add(nLowest);
    }
    return patterns.Count;
}

这个函数返回116。在我的机器上花费的时间是0.023ms。

编辑:您可以通过使用 4 次观察获得额外 7 倍的改进:

  1. 我们可以使用一个简单的访问数组来代替散列集。如果以前看到过某种模式,我们就不计算在内。这也消除了跟踪内部循环中“最低”模式的需要。如果一个模式被访问过,那么它的最低旋转模式也被访问过。
  2. 如果我们没有 180 度旋转对称,那么第 3 次旋转将不会产生原始图案。第 4 次轮换总是如此,因此没有必要。
  3. 旋转表达式可以稍微简化。

因此,如果我们应用这些观察结果并展开内部 do 循环,我们会得到以下结果:

static int Rotate(int n) {
    n = ((n & (1+32)) << 2) + ((n & 2) << 4) + ((n & 4) << 6)
        + ((n & (8+256)) >> 2) + (n & 16)
        + ((n & 64) >> 6) + ((n & 128) >> 4);
    while ((n & 73) == 0) 
        n >>= 1;
    return n;
}
public static int Count3x3_3() {
    bool[] visited = new bool[512];
    int count = 0, r;
    for (int i = 0; i < 512; i++) {
        if (visited[i])
            continue;
        if ((i & 7) == 0 || (i & 73) == 0)
            continue;
        count++;
        if ((r = Rotate(i)) == i) continue;
        visited[r] = true;
        if ((r = Rotate(r)) == i) continue;
        visited[r] = true;
        visited[Rotate(r)] = true;
    }
    return count;
}

这在同一台机器上运行大约 3μs。

【讨论】:

  • 我非常喜欢。这里;有一个赏金。
【解决方案2】:

我的解决方案:116 种独特的形状

在测试 2 个形状是否相等时,比较引脚数可以节省大量时间。但我最大的突破是意识到所有这 25 个位置都可以替换为:对于要检查是否相等的两个 3x3 形状中的每一个,用两个零连接线,然后修剪前导零和尾随零。连接零是为了防止回绕。示例:

010 => 01000 => 0100010100000 => 1000101
101    10100
000    000

000 => 00000 => 0000001000101 => 1000101
010    01000
101    101

然后只测试结果是否相等。这是 4 次简单的迭代(每次旋转 1 次),而不是更复杂的 100 次(25 个位置 * 4 次旋转)。


时间:
仅字符串:

  • 蛮力,每次旋转所有 25 个位置:2018 毫秒
  • ...00...00...修剪:75ms
  • 更多优化:59ms

OOP 和更好的缓存:17ms

【讨论】:

  • 这是一个很酷的想法,但它不会将 1100000000000 和 1000010000000 视为不同,而实际上它们是相同的(通过旋转)?
  • @MikeSokolov:是的,这就是为什么我说我必须这样做 4 次——每次轮换一次。但是 4 * 1 超出了我最初设计的 4 * 25 的想法。
  • 令人印象深刻的是,这种方法主要可以使用位算术和整数比较来实现。 30行Python下,30ms运行:pastebin.com/V8UV5J2f
  • @eswald 您的(非常优雅的)Python 脚本包括一个所有 9 个位置都是空白的脚本。那个应该从最终集合中删除。
  • @Dinah:是的。现在修复了,有两个字符。不过,我还没有完成将其结果与问题中的想法进行比较;对于某些形状,它们产生的最小值略有不同,但至少数量是相同的。
【解决方案3】:

首先,除了平移之外,我们可以将两个等效的出拳视为彼此的旋转。想象打孔图案位于球体的表面:我们可以通过沿水平轴和垂直轴旋转球体来“平移”它(就像它在我们手中一样。)

我们沿剩余的第三个轴旋转球体,在这里也捕获了相当于旋转(如 90 度转弯)的两个拳头。

现在我们已将问题简化为“球体表面有多少独特的打孔图案,直到旋转?”要计算像这样对称的唯一对象,您需要not-Burnside's LemmaThis book 是一本很好的入门书。

【讨论】:

  • 球体不允许环绕吗?
  • 这就是想法:环绕等同于平面中的翻译。
  • 但是根据 Mike Sokolov 的反例,环绕不适合这个问题。
  • 啊,好点子。在背面插入一个守卫/“无限远点”将空间分成四个象限可能会解决这个问题。在这一点上,球体的比喻是不合适的,你(和迈克)是对的。一会儿我看看能不能直接构造排列组,但现在不行。
【解决方案4】:

我不认为这就像球体案例,因为您不能围绕边缘旋转?即:

XOO
XXO
XOO

不一样

OOX
XOX
OOX

我试着在纸上用手数数,看看我得到了什么。考虑 2x2 的情况——你有 1 个有 0 个点,1 个有 1 个点,2 个有 2 个点(相邻或对角线),1 个有 3 个点,1 个有 4 个;总共 5 个(如果您忽略空壳,则为 4 个)。请注意,枚举是对称的,因为将空白空间计为完整空间是相同的。对于 3x3 案例,我得到了这个:

C(0) = 1
C(1) = 1
C(2) = 5
C(3) = 10
C(4) = 21

然后通过对称,21、10、5、1、1

我得到 76。我很容易数错,尤其是在 4/5 的情况下。

我能想到自动枚举这些的唯一方法是移动和旋转模式以查看它们是否与先前枚举的模式匹配。换档很棘手,因为你只能换档直到你“撞”到边缘。

【讨论】:

  • 没有对称性。示例:C(1) 总是看起来像一个针孔。 C(8) 将有 3 种组合,因为如果空白位于中间、边缘或角落,它将是不同的形状。
  • 哦 - 你是对的!这个问题变得越来越有趣
【解决方案5】:

值得指出的是,如果您真的需要每个形状“看起来”独一无二,那么无论它如何旋转或移动,您都没有多少选择余地。例如,一个单拳,无论它在网格中的哪个位置,看起来总是一样的。此外,假设一个方形网格和圆形引脚,并假设微小的间距差异 (√2) 微不足道,那么在对角线上的 2 个孔看起来与两个相邻引脚相同,因为观察者看到的只是两个紧靠在一起的孔。同样,对角线上的 3 看起来就像直线上的 3,这极大地限制了您的选择。

请注意,shape 可能是比 combination 更好的词,因为我们不在乎实际的组合是什么,只关心结果是什么形状在纸上。

我认为我们可以假设,无论形状如何,它都可以旋转和移动,以便打孔左上角的销(特别是如果您允许旋转 45 度),这使我们能够缩小搜索范围更进一步。我们使用以下规则完成此操作:

  1. 如果有角被打孔,旋转网格直到打孔的角在左上角
  2. 否则,将图案向上和向左移动尽可能远。
  3. 重复步骤 1
  4. 如果我们走到这一步,那么我们知道只有顶部中间位置被打孔(因为我们知道两个角都没有),在这种情况下,我们将图案旋转 45 度,使顶部中间位置现在是左上角. QED。

我对可能的形状进行了非常快速的纸笔蛮力搜索,看起来可行的选项列表非常少,您可以在几分钟内将它们全部列出。

【讨论】:

    【解决方案6】:

    您不是要求计算直到平移和旋转的唯一模式的数量,而是要求进行一致性测试。

    选择 3x3 网格的位串表示。我会逐行选择,自上而下。通过在对应的孔被打孔的位置设置一个位,我们现在可以将 9 位整数映射到打孔模式(反之亦然)。

    对于任何特定的表示,我们可以设计表示旋转和平移的位摆弄操作。在某些模式上执行某些翻译是非法的,因为我们希望避免“环绕”。

    正如旋转和平移是可逆的,我们的操作也是如此。如果两个动作将模式 A 映射到 B,然后将 B 映射到 C,我们当然可以组合动作来进行 A 到 C 的变换。什么都不做(恒等变换)也是合法的,所以我们可以从 A 到达 A。可达性因此,通过变换是打孔模式的等价关系,因此等价模式划分了空间。

    通过为每个打孔模式分配一个正整数分数的方法,我们可以调用良序原则:包含该模式的等价类将包含一个得分最低的唯一模式(包括平移和旋转)。我们将选择这个最小模式作为等价类的代表。如果两个模式具有相同的等价类代表,它们必然是全等的。如果不是,则它们必然不一致。

    给定一个模式,我们如何找到它的最小权重代表?通过检查,贪心算法不能保证有效。我们可以使用无数启发式优化算法中的一种,或者我们可以注意到我们只推送 9 位并彻底搜索空间。应该注意的是,出于同样的原因,这很适合被计算一次,然后永远被推到查找表中。

    这是详尽的搜索。请注意,通过适当的缓存,它仍然非常快(不到一秒。)

    #!/usr/bin/env ruby
    
    require 'set'
    
    class PunchPattern < String
      @@representatives = Hash.new do |h, k|
        equivalence_class = k.closure
        representative = equivalence_class.min
    
        k.closure.each do |node|
          h[node] = representative
        end
    
        representative
      end
    
      def initialize(s)
        raise "9 digits of 0 and 1, pls" unless s =~ /[01]{9}/
        super
      end
    
      def left
        return nil unless self =~ /0..0..0../
        PunchPattern.new([self[1...3], 0, self[4...6], 0, self[7...9], 0].join)
      end
    
      def right
        return nil unless self =~ /..0..0..0/
        PunchPattern.new([0, self[0...2], 0, self[3...5], 0, self[6...8]].join)
      end
    
      def up
        return nil unless self =~ /000....../
        PunchPattern.new([self[3...9], 0, 0, 0].join)
      end
    
      def down
        return nil unless self =~ /......000/
        PunchPattern.new([0, 0, 0, self[0...6]].join)
      end
    
      def turn
        PunchPattern.new([2, 5, 8, 1, 4, 7, 0, 3, 6].collect { |i| self[i].chr }.join)
      end
    
      def closure
        seen = Set.new([])
        frontier = Set.new([self])
    
        until frontier.empty?
          node = frontier.first
          frontier.delete(node)
          seen.add(node)
    
          %w{left right up down turn}.collect do |motion|
            node.send motion
          end.compact.each do |neighbor|
            frontier.add(neighbor) unless seen.include? neighbor
          end
        end
    
        seen
      end
    
      def representative
        self.class.representatives[self]
      end
    
      def self.representatives
        @@representatives
      end
    
    end
    
    (0...512).collect do |p|
      p = PunchPattern.new(p.to_s(2).rjust(9, '0'))
      [p.representative, p]
    end.inject(Hash.new { |h, k| h[k] = [] }) do |h, pair|
      h[pair.first] << pair.last
      h
    end.sort_by { |pair| pair.first }.each do |representative, patterns|
      puts patterns.collect { |p| p[0...3] + " " }.join
      puts patterns.collect { |p| p[3...6] + " " }.join
      puts patterns.collect { |p| p[6...9] + " " }.join
      puts
    end
    
    puts "#{PunchPattern.representatives.values.uniq.size} total congruence classes"
    

    生产

    $ ./congruence.rb 
    000 
    000 
    000 
    
    000 000 000 000 000 000 001 010 100 
    000 000 000 001 010 100 000 000 000 
    001 010 100 000 000 000 000 000 000 
    
    000 000 000 000 000 000 000 001 010 011 100 110 
    000 000 001 010 011 100 110 001 010 000 100 000 
    011 110 001 010 000 100 000 000 000 000 000 000 
    
    000 000 001 010 100 101 
    000 101 000 000 000 000 
    101 000 001 010 100 000 
    
    000 000 001 010 100 111 
    000 111 001 010 100 000 
    111 000 001 010 100 000 
    
    000 000 000 000 001 010 010 100 
    001 010 010 100 010 001 100 010 
    010 001 100 010 000 000 000 000 
    
    000 000 000 000 000 000 000 000 001 010 010 011 011 100 110 110 
    001 010 010 011 011 100 110 110 011 011 110 001 010 110 010 100 
    011 011 110 001 010 110 010 100 000 000 000 000 000 000 000 000 
    
    000 001 010 100 
    001 100 000 000 
    100 000 001 010 
    
    000 000 001 010 011 100 101 110 
    001 101 101 000 000 000 100 000 
    101 100 000 011 001 110 000 010 
    
    000 000 001 010 010 011 100 100 
    001 011 110 001 010 100 010 100 
    110 100 000 001 001 000 010 010 
    
    000 000 001 010 011 100 110 111 
    001 111 111 010 001 100 010 100 
    111 100 000 011 001 110 010 000 
    
    000 000 001 010 010 010 100 101 
    010 101 010 001 100 101 010 010 
    101 010 001 010 010 000 100 000 
    
    000 000 001 010 010 010 100 111 
    010 111 011 011 110 111 110 010 
    111 010 001 010 010 000 100 000 
    
    000 000 011 110 
    011 110 011 110 
    011 110 000 000 
    
    000 000 010 011 011 100 101 110 
    011 101 001 010 101 010 110 100 
    101 110 011 001 000 110 000 010 
    
    000 010 011 100 
    011 011 110 110 
    110 001 000 010 
    
    000 000 010 011 011 100 110 111 
    011 111 011 011 111 110 110 110 
    111 110 011 001 000 110 010 000 
    
    000 001 010 100 
    100 000 000 001 
    001 010 100 000 
    
    000 000 001 001 010 010 100 110 
    100 110 001 010 010 100 011 001 
    011 001 010 010 100 100 000 000 
    
    000 000 001 010 011 100 101 110 
    100 101 000 000 000 101 001 000 
    101 001 011 110 010 000 000 100 
    
    000 000 001 010 011 100 110 111 
    100 111 001 010 010 111 100 001 
    111 001 011 110 010 000 100 000 
    
    000 000 001 010 011 101 110 110 
    101 110 010 100 001 011 010 101 
    011 101 011 110 010 000 100 000 
    
    000 011 101 110 
    101 000 101 000 
    101 011 000 110 
    
    000 000 011 011 101 110 110 111 
    101 111 001 010 111 010 100 101 
    111 101 011 011 000 110 110 000 
    
    000 001 010 110 
    110 011 110 011 
    011 010 100 000 
    
    000 000 001 010 011 110 110 111 
    110 111 011 110 011 110 111 011 
    111 011 011 110 010 100 000 000 
    
    000 011 110 111 
    111 011 110 111 
    111 011 110 000 
    
    001 100 
    000 000 
    100 001 
    
    001 100 101 101 
    000 000 000 000 
    101 101 001 100 
    
    001 011 100 100 
    000 000 001 100 
    110 100 001 001 
    
    001 100 101 111 
    000 100 001 000 
    111 101 001 100 
    
    001 001 100 110 
    001 100 000 000 
    100 100 011 001 
    
    001 100 101 111 
    001 000 100 000 
    101 111 100 001 
    
    001 011 100 110 
    001 100 100 001 
    110 100 011 001 
    
    001 100 111 111 
    001 100 001 100 
    111 111 001 100 
    
    001 100 
    010 010 
    100 001 
    
    001 100 101 101 
    010 010 010 010 
    101 101 001 100 
    
    001 011 100 100 
    010 010 011 110 
    110 100 001 001 
    
    001 100 101 111 
    010 110 011 010 
    111 101 001 100 
    
    001 001 100 110 
    011 110 010 010 
    100 100 011 001 
    
    001 100 101 111 
    011 010 110 010 
    101 111 100 001 
    
    001 011 100 110 
    011 110 110 011 
    110 100 011 001 
    
    001 100 111 111 
    011 110 011 110 
    111 111 001 100 
    
    001 010 100 101 
    100 000 001 000 
    001 101 100 010 
    
    001 010 010 100 
    100 001 100 001 
    010 100 001 010 
    
    001 010 101 110 
    100 100 001 001 
    011 101 010 100 
    
    001 101 101 110 
    100 000 001 000 
    101 011 100 101 
    
    001 011 100 110 
    100 001 001 100 
    110 100 011 001 
    
    001 101 110 111 
    100 001 100 001 
    111 011 101 100 
    
    001 010 100 111 
    101 000 101 000 
    001 111 100 010 
    
    001 010 010 110 
    101 100 101 001 
    010 011 100 010 
    
    001 010 110 111 
    101 100 101 001 
    011 111 100 010 
    
    001 110 
    101 000 
    100 011 
    
    001 101 110 111 
    101 101 000 000 
    101 100 111 011 
    
    001 011 110 110 
    101 101 001 100 
    110 100 011 011 
    
    001 110 111 111 
    101 100 001 101 
    111 111 011 100 
    
    001 010 100 101 
    110 010 011 010 
    001 101 100 010 
    
    001 010 010 100 
    110 011 110 011 
    010 100 001 010 
    
    001 010 101 110 
    110 110 011 011 
    011 101 010 100 
    
    001 101 101 110 
    110 010 011 010 
    101 011 100 101 
    
    001 011 100 110 
    110 011 011 110 
    110 100 011 001 
    
    001 101 110 111 
    110 011 110 011 
    111 011 101 100 
    
    001 010 100 111 
    111 010 111 010 
    001 111 100 010 
    
    001 010 010 110 
    111 110 111 011 
    010 011 100 010 
    
    001 010 110 111 
    111 110 111 011 
    011 111 100 010 
    
    001 110 
    111 010 
    100 011 
    
    001 101 110 111 
    111 111 010 010 
    101 100 111 011 
    
    001 011 110 110 
    111 111 011 110 
    110 100 011 011 
    
    001 110 111 111 
    111 110 011 111 
    111 111 011 100 
    
    010 011 100 101 
    001 100 001 100 
    101 001 110 010 
    
    010 010 011 100 
    001 101 100 101 
    110 001 010 010 
    
    010 011 100 111 
    001 101 101 100 
    111 001 110 010 
    
    010 011 100 101 
    011 110 011 110 
    101 001 110 010 
    
    010 010 011 100 
    011 111 110 111 
    110 001 010 010 
    
    010 011 100 111 
    011 111 111 110 
    111 001 110 010 
    
    010 
    101 
    010 
    
    010 010 011 110 
    101 101 101 101 
    011 110 010 010 
    
    010 011 101 110 
    101 100 101 001 
    101 011 010 110 
    
    010 011 110 111 
    101 101 101 101 
    111 011 110 010 
    
    010 
    111 
    010 
    
    010 010 011 110 
    111 111 111 111 
    011 110 010 010 
    
    010 011 101 110 
    111 110 111 011 
    101 011 010 110 
    
    010 011 110 111 
    111 111 111 111 
    111 011 110 010 
    
    011 100 101 101 
    000 001 000 100 
    101 101 110 001 
    
    011 100 
    000 101 
    110 001 
    
    011 100 101 111 
    000 101 101 000 
    111 101 001 110 
    
    011 100 101 111 
    001 001 100 100 
    101 111 110 001 
    
    011 011 100 110 
    001 100 101 101 
    110 110 011 001 
    
    011 100 111 111 
    001 101 100 101 
    111 111 110 001 
    
    011 100 101 101 
    010 011 010 110 
    101 101 110 001 
    
    011 100 
    010 111 
    110 001 
    
    011 100 101 111 
    010 111 111 010 
    111 101 001 110 
    
    011 100 101 111 
    011 011 110 110 
    101 111 110 001 
    
    011 011 100 110 
    011 110 111 111 
    110 110 011 001 
    
    011 100 111 111 
    011 111 110 111 
    111 111 110 001 
    
    011 101 101 110 
    100 001 100 001 
    101 110 011 101 
    
    011 101 110 111 
    100 101 101 001 
    111 011 101 110 
    
    011 101 110 111 
    101 101 001 100 
    101 110 111 011 
    
    011 110 
    101 101 
    110 011 
    
    011 110 111 111 
    101 101 101 101 
    111 111 011 110 
    
    011 101 101 110 
    110 011 110 011 
    101 110 011 101 
    
    011 101 110 111 
    110 111 111 011 
    111 011 101 110 
    
    011 101 110 111 
    111 111 011 110 
    101 110 111 011 
    
    011 110 
    111 111 
    110 011 
    
    011 110 111 111 
    111 111 111 111 
    111 111 011 110 
    
    101 
    000 
    101 
    
    101 101 101 111 
    000 001 100 000 
    111 101 101 101 
    
    101 101 111 111 
    001 100 001 100 
    111 111 101 101 
    
    101 
    010 
    101 
    
    101 101 101 111 
    010 011 110 010 
    111 101 101 101 
    
    101 101 111 111 
    011 110 011 110 
    111 111 101 101 
    
    101 111 
    101 000 
    101 111 
    
    101 111 111 111 
    101 001 100 101 
    111 111 111 101 
    
    101 111 
    111 010 
    101 111 
    
    101 111 111 111 
    111 011 110 111 
    111 111 111 101 
    
    111 
    101 
    111 
    
    111 
    111 
    111 
    
    117 total congruence classes
    

    ..为 117 个课程。

    【讨论】:

    • 很好的解释。顺便说一句:你的计数是多少?我得了 116,eswald 得了 117。
    • 我得到 117,它们现在已内联。每行代表一个等价类。
    • 啊,我们不同意空模式。你的问题陈述把它扔掉了。
    【解决方案7】:

    我不太清楚这部分关于旋转的内容,但对于这种情况:

    有3x3=9个孔,10个案例,每次只能发生一个案例:

    Case 1 = no holes
    Case 2 = one hole
    ...
    Case 10 = 9 holes
    

    那么结合公式 C(n,k) 会是这样的:

    总计 = C(9,0)+C(9,1)+....+C(9,9)

    这是对 n 个元素的 k 个不同组合求和。

    总计 = 1+9+36+84+126+126+84+36+9+1 = 512

    我认为你是对的,512 似乎是正确的,理论上无针也被定义为一个组合,如果你不希望它删除它以获得 511。

    所有这些情况都是单独发生的,所以我们添加了不同的情况。

    如果它们同时发生,例如计算 3 名员工打孔一次的组合或计算一名员工 3 次标记纸的组合,那么它会变得更加复杂,应该是 512 * 512 * 512 nxm 规则。

    简单显示中的 m*n 规则是:

    我有 4=m 个口袋和两个=n 手:

    my_left * 4(pockets)=4 my_right * 4(pockets)=4

    总计= 4+4=4*2=m*n

    一次只能一只手进入口袋,或者只有一名员工的一种组合与另一名员工的一种组合配对,这就是我们采用 m*n 规则的确切原因。

    这就是我的想法,我不是数学家,也不声称100%正确,这只是我从概率课程中记得的。

    我并不声称自己是 100% 正确的,这只是我对概率课程的记忆。


    至于模式重叠,检查是否已经找到模式等我根本不会打扰,因为伪代码中有很多算法可以生成组合。由于它们生成,因此无需检查重叠或任何东西。

    毕竟这就是生成的意思,它是先验设计的,可以找到所有结果,让它运行。

    我曾经发现过一种比简单组合更罕见的算法,当我将它转换为 php 时,它完美地完成了这项工作,无需担心重叠或任何事情。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-26
      • 1970-01-01
      相关资源
      最近更新 更多