3

我需要生成 N 个数字的所有可能组合,包括重复。

问题输入:我有 N 个单元格,我可以在每个单元格中的区间 0 到:9 中放置一个数字。

错误的解决方案(N = 4):

(0 to: 3) permutationsDo: [ : each | Transcript cr; show: each printString].

不包括 #(0 0 0 0) 、 #(1 1 1 1) 、 #(2 2 2 2) 等。

预期输出(为简洁起见,N = 2,范围为 1-4):

#(1 1)
#(2 2)
#(3 3)
#(4 4)
#(2 1)
#(3 2)
#(4 3)
#(1 4)
#(3 1)
#(4 2)
#(1 3)
#(2 4)
#(4 1)
#(1 2)
#(2 3)
#(3 4)
4

2 回答 2

2

SequenceableCollection为了简单起见,让我实现它:

nextCombination09
    | j |
    j := self findLast: [:ai | ai < 9] ifAbsent: [^nil].
    j + 1 to: self size do: [:i | self at: i put: 0].
    self at: j put: (self at: j) + 1

想法如下:使用字典顺序对所有组合进行排序。换句话说:

(a1, ..., an) < (b1, ..., bn)

如果ai = bi对于所有i低于某个索引jwhere aj < bj

使用此顺序,第一个组合是(0, ..., 0)和最后一个组合(9, ..., 9)

此外,给定一个组合(a1, ..., an),按此顺序排列的下一个是添加1到最低卓越索引的组合,这是最后一个索引j,其中aj < 9. 例如,下一个 to(2, 3, 8, 9)(2, 3, 9, 9)因为两者之间不能有任何东西。

一旦我们到达,(9, ..., 9)我们就完成了,并用 回答nil

请注意,上面的方法修改了接收器,这就是为什么我们必须copy在下面的脚本中。

这是产生所有组合的脚本(n是你的N

  n := <whatever>
  array := Array new: n withAll: 0.
  combinations := OrderedCollection new: (10 raisedTo: n).
  [
    combinations add: array copy.
    array nextCombination09 notNil] whileTrue.
  ^combinations

附录

相同的技术可用于其他类似性质的问题。

于 2016-05-14T21:36:52.053 回答
2

这里有几个选择器可以用来扩展SequenceableCollection. 那permutationsDo:是定义并最终由该类继承的Interval类。

“枚举”类别:

enumerationsDo: aBlock
   | anArray |
   anArray := Array new: self size.
   self enumerateWithSize: (self size) in: anArray do: [ :each | aBlock value: each ]

类别“私人”:

enumerateWithSize: aSize in: anArray do: aBlock
   (aSize = 1)
      ifTrue: [
         self do: [ :each |
            aBlock value: (anArray at: (self size - aSize + 1) put: each ; yourself) ] ]
      ifFalse: [
         self do: [ :each |
            self enumerateWithSize: (aSize - 1) in: anArray do: [ :eachArray |
               aBlock value: (eachArray at: (self size - aSize + 1) put: each ; yourself) ] ] ]

所以现在你可以这样做:

(0 to: 2) enumerationsDo: [ :each | Transcript show: each printString ; cr ]

产生:

#(0 0 0)
#(0 0 1)
#(0 0 2)
#(0 1 0)
#(0 1 1)
#(0 1 2)
#(0 2 0)
#(0 2 1)
#(0 2 2)
#(1 0 0)
#(1 0 1)
#(1 0 2)
#(1 1 0)
#(1 1 1)
#(1 1 2)
#(1 2 0)
#(1 2 1)
#(1 2 2)
#(2 0 0)
#(2 0 1)
#(2 0 2)
#(2 1 0)
#(2 1 1)
#(2 1 2)
#(2 2 0)
#(2 2 1)
#(2 2 2)

这个选择器像现有选择器一样“对称地”操作permutationsDo:,即结果数组中的元素数(选择数)与集合中的值数相同。


您可以轻松地从该解决方案转到更通用的解决方案:

在“枚举”下:

enumerationsDo: aBlock
    self enumerationsOfSize: (self size) do: aBlock

enumerationsOfSize: aSize do: aBlock
   | anArray |
   anArray := Array new: aSize.
   self enumerateWithSize: aSize subSize: aSize in: anArray do: [ :each | aBlock value: each ]

在“私人”下:

enumerateWithSize: aSize subSize: sSize in: anArray do: aBlock
    (aSize < sSize)
        ifTrue: [ ^self error: 'subSize cannot exceed array size' ].
    (sSize < 1)
        ifTrue: [ ^self error: 'sizes must be positive' ].

    (sSize = 1)
        ifTrue: [
            self do: [ :each |
                aBlock value: (anArray at: (aSize - sSize + 1) put: each ; yourself) ] ]
        ifFalse: [
            self do: [ :each |
                self enumerateWithSize: aSize subSize: (sSize - 1) in: anArray do: [ :eachArray |
                    aBlock value: (eachArray at: (aSize - sSize + 1) put: each ; yourself) ] ] ]

这是一个例子:

(1 to: 3) enumerationsOfSize: 2 do: [ :e | Transcript show: e printString ; cr ]

产生:

#(1 1)
#(1 2)
#(1 3)
#(2 1)
#(2 2)
#(2 3)
#(3 1)
#(3 2)
#(3 3)
于 2016-05-15T01:25:32.687 回答