题
我想写一个功能,需要一系列信件作为参数,一定数量的那些字母的选择。
说你提供一系列8中的信函并要选择3个字母。那么你应该得到:
8! / ((8 - 3)! * 3!) = 56
阵列(或字)在返回组成的3个字母的每一个。
解决方案
Chase's Twiddle(算法)
Phillip J Chase,`算法382:N个对象中M的组合'(1970)
C中的算法 ......
字典顺序中的组合索引(带扣算法515)
您还可以通过其索引(按字典顺序)引用组合。根据索引意识到索引应该是从右到左的一些变化,我们可以构造一些应该恢复组合的东西。
所以,我们有一套{1,2,3,4,5,6} ......我们想要三个元素。让我们说{1,2,3}我们可以说元素之间的差异是一个,有序和最小。 {1,2,4}有一个变化,按字典顺序排列第2位。因此,最后一个地方的“变化”数量占字典顺序的一个变化。第二个位置,只有一个变化{1,3,4}有一个变化,但由于它位于第二位(与原始集合中的元素数量成比例),因此会有更多变化。
我所描述的方法是解构,似乎从设置到索引,我们需要做反向<!>#8211;这更棘手。这就是 Buckles 解决问题的方法。我写了一些 C来计算它们,稍作修改< >!#8211;我使用集合的索引而不是数字范围来表示集合,因此我们总是从0 ... n开始工作。 注意:
- 由于组合是无序的,{1,3,2} = {1,2,3} - 我们将它们命名为词典。
- 此方法有一个隐式0来启动第一个差异的设置。 醇>
词典中的组合索引hical Order(McCaffrey)
另一种方式:它的概念更容易掌握和编程,但它没有Buckles的优化。幸运的是,它也不会产生重复的组合:
举个例子:27 = C(6,4) + C(5,3) + C(2,2) + C(1,1)
。因此,第四十七个词典组合的四个事物是:{1,2,5,6},这些是你想要看的任何集合的索引。下面的示例(OCaml),需要choose
功能,留给读者:
(* this will find the [x] combination of a [set] list when taking [k] elements *)
let combination_maccaffery set k x =
(* maximize function -- maximize a that is aCb *)
(* return largest c where c < i and choose(c,i) <= z *)
let rec maximize a b x =
if (choose a b ) <= x then a else maximize (a-1) b x
in
let rec iterate n x i = match i with
| 0 -> []
| i ->
let max = maximize n i x in
max :: iterate n (x - (choose max i)) (i-1)
in
if x < 0 then failwith "errors" else
let idxs = iterate (List.length set) x k in
List.map (List.nth set) (List.sort (-) idxs)
一个小而简单的组合迭代器
提供以下两种算法用于教学目的。它们实现了迭代器和(更一般的)文件夹整体组合。
它们尽可能快,具有复杂度O( n C k )。内存消耗受k
约束。
我们将从迭代器开始,它将为每个组合调用用户提供的函数
let iter_combs n k f =
let rec iter v s j =
if j = k then f v
else for i = s to n - 1 do iter (i::v) (i+1) (j+1) done in
iter [] 0 0
更通用的版本将从初始状态开始调用用户提供的函数以及状态变量。因为我们需要在不同状态之间传递状态,所以我们不会使用for循环,而是使用递归,
let fold_combs n k f x =
let rec loop i s c x =
if i < n then
loop (i+1) s c @@
let c = i::c and s = s + 1 and i = i + 1 in
if s < k then loop i s c x else f c x
else x in
loop 0 0 [] x
其他提示
在C#中:
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k)
{
return k == 0 ? new[] { new T[0] } :
elements.SelectMany((e, i) =>
elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] {e}).Concat(c)));
}
用法:
var result = Combinations(new[] { 1, 2, 3, 4, 5 }, 3);
结果:
123
124
125
134
135
145
234
235
245
345
简短的java解决方案:
import java.util.Arrays;
public class Combination {
public static void main(String[] args){
String[] arr = {"A","B","C","D","E","F"};
combinations2(arr, 3, 0, new String[3]);
}
static void combinations2(String[] arr, int len, int startPosition, String[] result){
if (len == 0){
System.out.println(Arrays.toString(result));
return;
}
for (int i = startPosition; i <= arr.length-len; i++){
result[result.length - len] = arr[i];
combinations2(arr, len-1, i+1, result);
}
}
}
结果将是
[A, B, C]
[A, B, D]
[A, B, E]
[A, B, F]
[A, C, D]
[A, C, E]
[A, C, F]
[A, D, E]
[A, D, F]
[A, E, F]
[B, C, D]
[B, C, E]
[B, C, F]
[B, D, E]
[B, D, F]
[B, E, F]
[C, D, E]
[C, D, F]
[C, E, F]
[D, E, F]
我可以提出我的递归Python解决方案来解决这个问题吗?
def choose_iter(elements, length):
for i in xrange(len(elements)):
if length == 1:
yield (elements[i],)
else:
for next in choose_iter(elements[i+1:len(elements)], length-1):
yield (elements[i],) + next
def choose(l, k):
return list(choose_iter(l, k))
使用示例:
>>> len(list(choose_iter("abcdefgh",3)))
56
我喜欢它的简单。
让我们说你的字母数组如下:<!> quot; ABCDEFGH <!> quot;。你有三个索引(i,j,k)表示你将用于当前单词的字母,你可以从:
开始A B C D E F G H ^ ^ ^ i j k
首先你改变k,所以下一步看起来像这样:
A B C D E F G H ^ ^ ^ i j k
如果你到达终点,你继续改变j然后再改变k。
A B C D E F G H ^ ^ ^ i j k A B C D E F G H ^ ^ ^ i j k
一旦你到达G,你也开始改变我。
A B C D E F G H ^ ^ ^ i j k A B C D E F G H ^ ^ ^ i j k ...
用代码编写,看起来像那样
void print_combinations(const char *string)
{
int i, j, k;
int len = strlen(string);
for (i = 0; i < len - 2; i++)
{
for (j = i + 1; j < len - 1; j++)
{
for (k = j + 1; k < len; k++)
printf("%c%c%c\n", string[i], string[j], string[k]);
}
}
}
以下递归算法挑选所有的k-元件组合从一个有序的设置:
- 选择的第一个元素
i
你结合 - 相结合
i
每个组合k-1
元素选择递归集的元素比较大i
.
迭代上述每 i
在设定。
至关重要的是,你选择的其他因素作比较大 i
, ,避免重复。这种方式[3,5]将只一次,作为[3]结合[5],而不是两次(条件消除了[5]+[3]).没有这个条件你得到变化而不是组合。
我发现这个线程很有用,并且我认为我会添加一个Javascript解决方案,你可以将其弹出到Firebug中。根据您的JS引擎,如果起始字符串很大,可能需要一些时间。
function string_recurse(active, rest) {
if (rest.length == 0) {
console.log(active);
} else {
string_recurse(active + rest.charAt(0), rest.substring(1, rest.length));
string_recurse(active, rest.substring(1, rest.length));
}
}
string_recurse("", "abc");
输出应如下:
abc
ab
ac
a
bc
b
c
在C ++中,以下例程将生成[first,last]范围之间的长度距离(first,k)的所有组合:
#include <algorithm>
template <typename Iterator>
bool next_combination(const Iterator first, Iterator k, const Iterator last)
{
/* Credits: Mark Nelson http://marknelson.us */
if ((first == last) || (first == k) || (last == k))
return false;
Iterator i1 = first;
Iterator i2 = last;
++i1;
if (last == i1)
return false;
i1 = last;
--i1;
i1 = k;
--i2;
while (first != i1)
{
if (*--i1 < *i2)
{
Iterator j = k;
while (!(*i1 < *j)) ++j;
std::iter_swap(i1,j);
++i1;
++j;
i2 = k;
std::rotate(i1,j,last);
while (last != j)
{
++j;
++i2;
}
std::rotate(k,i2,last);
return true;
}
}
std::rotate(first,k,last);
return false;
}
可以像这样使用:
#include <string>
#include <iostream>
int main()
{
std::string s = "12345";
std::size_t comb_size = 3;
do
{
std::cout << std::string(s.begin(), s.begin() + comb_size) << std::endl;
} while (next_combination(s.begin(), s.begin() + comb_size, s.end()));
return 0;
}
这将打印以下内容:
123
124
125
134
135
145
234
235
245
345
static IEnumerable<string> Combinations(List<string> characters, int length)
{
for (int i = 0; i < characters.Count; i++)
{
// only want 1 character, just return this one
if (length == 1)
yield return characters[i];
// want more than one character, return this one plus all combinations one shorter
// only use characters after the current one for the rest of the combinations
else
foreach (string next in Combinations(characters.GetRange(i + 1, characters.Count - (i + 1)), length - 1))
yield return characters[i] + next;
}
}
简短的例子,在蟒蛇:
def comb(sofar, rest, n):
if n == 0:
print sofar
else:
for i in range(len(rest)):
comb(sofar + rest[i], rest[i+1:], n-1)
>>> comb("", "abcde", 3)
abc
abd
abe
acd
ace
ade
bcd
bce
bde
cde
为了解释说明,递归的方法是描述下列例子:
例如:A B C D E
所有组合的3将是:
- 一个与所有组合中的2个从其余部分(B C D E)
- B所有的组合,2从其余部分(C D E)
- C与所有组合中的2个从其余部分(D E)
Haskell中的简单递归算法
import Data.List
combinations 0 lst = [[]]
combinations n lst = do
(x:xs) <- tails lst
rest <- combinations (n-1) xs
return $ x : rest
我们首先定义特殊情况,即选择零元素。它产生一个结果,这是一个空列表(即包含空列表的列表)。
对于n <!> gt; 0,x
遍历列表的每个元素,xs
是rest
之后的每个元素。
n - 1
使用递归调用combinations
从x : rest
中选择<=>元素。函数的最终结果是一个列表,其中每个元素都是<=>(即<=>作为头部,<=>作为尾部的列表),用于<=>和<=>的每个不同值。
> combinations 3 "abcde"
["abc","abd","abe","acd","ace","ade","bcd","bce","bde","cde"]
当然,由于Haskell很懒,所以列表会根据需要逐步生成,因此您可以部分评估指数级大组合。
> let c = combinations 8 "abcdefghijklmnopqrstuvwxyz"
> take 10 c
["abcdefgh","abcdefgi","abcdefgj","abcdefgk","abcdefgl","abcdefgm","abcdefgn",
"abcdefgo","abcdefgp","abcdefgq"]
这就是爷爷COBOL,这是一种备受诽谤的语言。
让我们假设一个包含34个元素的数组,每个元素8个字节(纯粹是任意选择。)这个想法是枚举所有可能的4元素组合并将它们加载到数组中。
我们使用4个指数,每个位置对应4个组中的每个位置
数组的处理方式如下:
idx1 = 1
idx2 = 2
idx3 = 3
idx4 = 4
我们将idx4从4改为结尾。对于每个idx4,我们得到一个独特的组合 四人小组。当idx4到达数组的末尾时,我们将idx3递增1并将idx4设置为idx3 + 1。然后我们再次运行idx4到最后。我们以这种方式进行,分别扩充idx3,idx2和idx1,直到idx1的位置从数组的末尾开始小于4。这完成了算法。
1 --- pos.1
2 --- pos 2
3 --- pos 3
4 --- pos 4
5
6
7
etc.
第一次迭代:
1234
1235
1236
1237
1245
1246
1247
1256
1257
1267
etc.
COBOL示例:
01 DATA_ARAY.
05 FILLER PIC X(8) VALUE "VALUE_01".
05 FILLER PIC X(8) VALUE "VALUE_02".
etc.
01 ARAY_DATA OCCURS 34.
05 ARAY_ITEM PIC X(8).
01 OUTPUT_ARAY OCCURS 50000 PIC X(32).
01 MAX_NUM PIC 99 COMP VALUE 34.
01 INDEXXES COMP.
05 IDX1 PIC 99.
05 IDX2 PIC 99.
05 IDX3 PIC 99.
05 IDX4 PIC 99.
05 OUT_IDX PIC 9(9).
01 WHERE_TO_STOP_SEARCH PIC 99 COMP.
* Stop the search when IDX1 is on the third last array element:
COMPUTE WHERE_TO_STOP_SEARCH = MAX_VALUE - 3
MOVE 1 TO IDX1
PERFORM UNTIL IDX1 > WHERE_TO_STOP_SEARCH
COMPUTE IDX2 = IDX1 + 1
PERFORM UNTIL IDX2 > MAX_NUM
COMPUTE IDX3 = IDX2 + 1
PERFORM UNTIL IDX3 > MAX_NUM
COMPUTE IDX4 = IDX3 + 1
PERFORM UNTIL IDX4 > MAX_NUM
ADD 1 TO OUT_IDX
STRING ARAY_ITEM(IDX1)
ARAY_ITEM(IDX2)
ARAY_ITEM(IDX3)
ARAY_ITEM(IDX4)
INTO OUTPUT_ARAY(OUT_IDX)
ADD 1 TO IDX4
END-PERFORM
ADD 1 TO IDX3
END-PERFORM
ADD 1 TO IDX2
END_PERFORM
ADD 1 TO IDX1
END-PERFORM.
这是Scala中优雅的通用实现,如 99 Scala问题中所述。
object P26 {
def flatMapSublists[A,B](ls: List[A])(f: (List[A]) => List[B]): List[B] =
ls match {
case Nil => Nil
case sublist@(_ :: tail) => f(sublist) ::: flatMapSublists(tail)(f)
}
def combinations[A](n: Int, ls: List[A]): List[List[A]] =
if (n == 0) List(Nil)
else flatMapSublists(ls) { sl =>
combinations(n - 1, sl.tail) map {sl.head :: _}
}
}
如果您可以使用SQL语法 - 例如,如果您使用LINQ访问结构或数组的字段,或者直接访问具有名为<!>的表的数据库,则使用; <!>只有一个字段<!>“;字母<!>”,您可以调整以下代码:
SELECT A.Letter, B.Letter, C.Letter
FROM Alphabet AS A, Alphabet AS B, Alphabet AS C
WHERE A.Letter<>B.Letter AND A.Letter<>C.Letter AND B.Letter<>C.Letter
AND A.Letter<B.Letter AND B.Letter<C.Letter
这将返回3个字母的所有组合,尽管表格中有多少个字母<!>“Alphabet <!>”; (它可以是3,8,10,27等)。
如果您想要的是所有排列,而不是组合(即您希望<!>“ACB <!>”和<!>“ABC <!>”)计算为不同,而不是仅仅出现一次)只删除最后一行(AND一次)并完成。
后期编辑:在重新阅读问题之后,我意识到需要的是通用算法,而不仅仅是选择3个项目的特定算法。亚当休斯的答案是完整的,不幸的是我不能投票(还)。这个答案很简单,但只适用于你想要的3个项目。
另一个具有懒惰生成组合索引的C#版本。此版本维护单个索引数组,以定义所有值列表与当前组合值之间的映射,即在整个运行时期间不断使用 O(k)附加空间。该代码在 O(k)时间内生成单独的组合,包括第一个组合。
public static IEnumerable<T[]> Combinations<T>(this T[] values, int k)
{
if (k < 0 || values.Length < k)
yield break; // invalid parameters, no combinations possible
// generate the initial combination indices
var combIndices = new int[k];
for (var i = 0; i < k; i++)
{
combIndices[i] = i;
}
while (true)
{
// return next combination
var combination = new T[k];
for (var i = 0; i < k; i++)
{
combination[i] = values[combIndices[i]];
}
yield return combination;
// find first index to update
var indexToUpdate = k - 1;
while (indexToUpdate >= 0 && combIndices[indexToUpdate] >= values.Length - k + indexToUpdate)
{
indexToUpdate--;
}
if (indexToUpdate < 0)
yield break; // done
// update combination indices
for (var combIndex = combIndices[indexToUpdate] + 1; indexToUpdate < k; indexToUpdate++, combIndex++)
{
combIndices[indexToUpdate] = combIndex;
}
}
}
测试代码:
foreach (var combination in new[] {'a', 'b', 'c', 'd', 'e'}.Combinations(3))
{
System.Console.WriteLine(String.Join(" ", combination));
}
输出:
a b c
a b d
a b e
a c d
a c e
a d e
b c d
b c e
b d e
c d e
这里有一个用C#编码的算法的惰性评估版本:
static bool nextCombination(int[] num, int n, int k)
{
bool finished, changed;
changed = finished = false;
if (k > 0)
{
for (int i = k - 1; !finished && !changed; i--)
{
if (num[i] < (n - 1) - (k - 1) + i)
{
num[i]++;
if (i < k - 1)
{
for (int j = i + 1; j < k; j++)
{
num[j] = num[j - 1] + 1;
}
}
changed = true;
}
finished = (i == 0);
}
}
return changed;
}
static IEnumerable Combinations<T>(IEnumerable<T> elements, int k)
{
T[] elem = elements.ToArray();
int size = elem.Length;
if (k <= size)
{
int[] numbers = new int[k];
for (int i = 0; i < k; i++)
{
numbers[i] = i;
}
do
{
yield return numbers.Select(n => elem[n]);
}
while (nextCombination(numbers, size, k));
}
}
测试部分:
static void Main(string[] args)
{
int k = 3;
var t = new[] { "dog", "cat", "mouse", "zebra"};
foreach (IEnumerable<string> i in Combinations(t, k))
{
Console.WriteLine(string.Join(",", i));
}
}
希望这对你有帮助!
我有一个用于项目euler的排列算法,在python中:
def missing(miss,src):
"Returns the list of items in src not present in miss"
return [i for i in src if i not in miss]
def permutation_gen(n,l):
"Generates all the permutations of n items of the l list"
for i in l:
if n<=1: yield [i]
r = [i]
for j in permutation_gen(n-1,missing([i],l)): yield r+j
如果
n<len(l)
你应该拥有所需的所有组合而不重复,你需要吗?
它是一个生成器,所以你可以使用它:
for comb in permutation_gen(3,list("ABCDEFGH")):
print comb
https://gist.github.com/3118596
JavaScript有一个实现。它具有获取k组合和任何对象数组的所有组合的功能。例子:
k_combinations([1,2,3], 2)
-> [[1,2], [1,3], [2,3]]
combinations([1,2,3])
-> [[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]]
Array.prototype.combs = function(num) {
var str = this,
length = str.length,
of = Math.pow(2, length) - 1,
out, combinations = [];
while(of) {
out = [];
for(var i = 0, y; i < length; i++) {
y = (1 << i);
if(y & of && (y !== of))
out.push(str[i]);
}
if (out.length >= num) {
combinations.push(out);
}
of--;
}
return combinations;
}
Clojure版本:
(defn comb [k l]
(if (= 1 k) (map vector l)
(apply concat
(map-indexed
#(map (fn [x] (conj x %2))
(comb (dec k) (drop (inc %1) l)))
l))))
让我们说你的字母数组如下所示:<!> quot; ABCDEFGH <!> quot;。你有三个索引(i,j,k)表示你将用于当前单词的字母,你可以从:
开始A B C D E F G H ^ ^ ^ i j k
首先你改变k,所以下一步看起来像这样:
A B C D E F G H ^ ^ ^ i j k
如果你到达终点,你继续改变j然后再改变k。
A B C D E F G H ^ ^ ^ i j k A B C D E F G H ^ ^ ^ i j k
一旦你到达G,你也开始改变我。
A B C D E F G H ^ ^ ^ i j k A B C D E F G H ^ ^ ^ i j k ...
function initializePointers($cnt) {
$pointers = [];
for($i=0; $i<$cnt; $i++) {
$pointers[] = $i;
}
return $pointers;
}
function incrementPointers(&$pointers, &$arrLength) {
for($i=0; $i<count($pointers); $i++) {
$currentPointerIndex = count($pointers) - $i - 1;
$currentPointer = $pointers[$currentPointerIndex];
if($currentPointer < $arrLength - $i - 1) {
++$pointers[$currentPointerIndex];
for($j=1; ($currentPointerIndex+$j)<count($pointers); $j++) {
$pointers[$currentPointerIndex+$j] = $pointers[$currentPointerIndex]+$j;
}
return true;
}
}
return false;
}
function getDataByPointers(&$arr, &$pointers) {
$data = [];
for($i=0; $i<count($pointers); $i++) {
$data[] = $arr[$pointers[$i]];
}
return $data;
}
function getCombinations($arr, $cnt)
{
$len = count($arr);
$result = [];
$pointers = initializePointers($cnt);
do {
$result[] = getDataByPointers($arr, $pointers);
} while(incrementPointers($pointers, count($arr)));
return $result;
}
$result = getCombinations([0, 1, 2, 3, 4, 5], 3);
print_r($result);
基于 https://stackoverflow.com/a/127898/2628125 ,但更抽象,适用于任何尺寸指针。
我为此在SQL Server 2005中创建了一个解决方案,并将其发布在我的网站上: http://www.jessemclain.com/downloads/code/sql/fn_GetMChooseNCombos.sql.htm
以下是显示用法的示例:
SELECT * FROM dbo.fn_GetMChooseNCombos('ABCD', 2, '')
结果:
Word
----
AB
AC
AD
BC
BD
CD
(6 row(s) affected)
这是我在C ++中的命题
我试图对迭代器类型施加尽可能少的限制,因此这个解决方案假设只是前向迭代器,它可以是一个const_iterator。这适用于任何标准容器。如果参数没有意义,它会抛出std :: invalid_argumnent
#include <vector>
#include <stdexcept>
template <typename Fci> // Fci - forward const iterator
std::vector<std::vector<Fci> >
enumerate_combinations(Fci begin, Fci end, unsigned int combination_size)
{
if(begin == end && combination_size > 0u)
throw std::invalid_argument("empty set and positive combination size!");
std::vector<std::vector<Fci> > result; // empty set of combinations
if(combination_size == 0u) return result; // there is exactly one combination of
// size 0 - emty set
std::vector<Fci> current_combination;
current_combination.reserve(combination_size + 1u); // I reserve one aditional slot
// in my vector to store
// the end sentinel there.
// The code is cleaner thanks to that
for(unsigned int i = 0u; i < combination_size && begin != end; ++i, ++begin)
{
current_combination.push_back(begin); // Construction of the first combination
}
// Since I assume the itarators support only incrementing, I have to iterate over
// the set to get its size, which is expensive. Here I had to itrate anyway to
// produce the first cobination, so I use the loop to also check the size.
if(current_combination.size() < combination_size)
throw std::invalid_argument("combination size > set size!");
result.push_back(current_combination); // Store the first combination in the results set
current_combination.push_back(end); // Here I add mentioned earlier sentinel to
// simplyfy rest of the code. If I did it
// earlier, previous statement would get ugly.
while(true)
{
unsigned int i = combination_size;
Fci tmp; // Thanks to the sentinel I can find first
do // iterator to change, simply by scaning
{ // from right to left and looking for the
tmp = current_combination[--i]; // first "bubble". The fact, that it's
++tmp; // a forward iterator makes it ugly but I
} // can't help it.
while(i > 0u && tmp == current_combination[i + 1u]);
// Here is probably my most obfuscated expression.
// Loop above looks for a "bubble". If there is no "bubble", that means, that
// current_combination is the last combination, Expression in the if statement
// below evaluates to true and the function exits returning result.
// If the "bubble" is found however, the ststement below has a sideeffect of
// incrementing the first iterator to the left of the "bubble".
if(++current_combination[i] == current_combination[i + 1u])
return result;
// Rest of the code sets posiotons of the rest of the iterstors
// (if there are any), that are to the right of the incremented one,
// to form next combination
while(++i < combination_size)
{
current_combination[i] = current_combination[i - 1u];
++current_combination[i];
}
// Below is the ugly side of using the sentinel. Well it had to haave some
// disadvantage. Try without it.
result.push_back(std::vector<Fci>(current_combination.begin(),
current_combination.end() - 1));
}
}
所有人都说过并完成了O'caml代码。 从代码中可以看出算法很明显。
let combi n lst =
let rec comb l c =
if( List.length c = n) then [c] else
match l with
[] -> []
| (h::t) -> (combi t (h::c))@(combi t c)
in
combi lst []
;;
这是我最近在Java中编写的代码,它计算并返回<!> quot; num <!> quot;的所有组合。来自<!>“outOf <!>的元素元件。
// author: Sourabh Bhat (heySourabh@gmail.com)
public class Testing
{
public static void main(String[] args)
{
// Test case num = 5, outOf = 8.
int num = 5;
int outOf = 8;
int[][] combinations = getCombinations(num, outOf);
for (int i = 0; i < combinations.length; i++)
{
for (int j = 0; j < combinations[i].length; j++)
{
System.out.print(combinations[i][j] + " ");
}
System.out.println();
}
}
private static int[][] getCombinations(int num, int outOf)
{
int possibilities = get_nCr(outOf, num);
int[][] combinations = new int[possibilities][num];
int arrayPointer = 0;
int[] counter = new int[num];
for (int i = 0; i < num; i++)
{
counter[i] = i;
}
breakLoop: while (true)
{
// Initializing part
for (int i = 1; i < num; i++)
{
if (counter[i] >= outOf - (num - 1 - i))
counter[i] = counter[i - 1] + 1;
}
// Testing part
for (int i = 0; i < num; i++)
{
if (counter[i] < outOf)
{
continue;
} else
{
break breakLoop;
}
}
// Innermost part
combinations[arrayPointer] = counter.clone();
arrayPointer++;
// Incrementing part
counter[num - 1]++;
for (int i = num - 1; i >= 1; i--)
{
if (counter[i] >= outOf - (num - 1 - i))
counter[i - 1]++;
}
}
return combinations;
}
private static int get_nCr(int n, int r)
{
if(r > n)
{
throw new ArithmeticException("r is greater then n");
}
long numerator = 1;
long denominator = 1;
for (int i = n; i >= r + 1; i--)
{
numerator *= i;
}
for (int i = 2; i <= n - r; i++)
{
denominator *= i;
}
return (int) (numerator / denominator);
}
}
一个简洁的Javascript解决方案:
Array.prototype.combine=function combine(k){
var toCombine=this;
var last;
function combi(n,comb){
var combs=[];
for ( var x=0,y=comb.length;x<y;x++){
for ( var l=0,m=toCombine.length;l<m;l++){
combs.push(comb[x]+toCombine[l]);
}
}
if (n<k-1){
n++;
combi(n,combs);
} else{last=combs;}
}
combi(1,toCombine);
return last;
}
// Example:
// var toCombine=['a','b','c'];
// var results=toCombine.combine(4);
这是一个方法,它为您提供随机长度字符串中指定大小的所有组合。与quinmars的解决方案类似,但适用于各种输入和k。
代码可以更改为环绕,即输入'abcd'的'dab'w k = 3.
public void run(String data, int howMany){
choose(data, howMany, new StringBuffer(), 0);
}
//n choose k
private void choose(String data, int k, StringBuffer result, int startIndex){
if (result.length()==k){
System.out.println(result.toString());
return;
}
for (int i=startIndex; i<data.length(); i++){
result.append(data.charAt(i));
choose(data,k,result, i+1);
result.setLength(result.length()-1);
}
}
输出<!>“; abcde <!>”;:
abc abd abe acd ace ade bcd bce bde cde
我编写了一个类来处理使用二项式系数的常用函数,这是您的问题所处的问题类型。它执行以下任务:
-
以任意N选择K到文件的格式输出所有K索引。 K索引可以用更具描述性的字符串或字母代替。这种方法使解决这类问题变得非常简单。
-
将K索引转换为已排序二项系数表中条目的正确索引。这种技术比依赖迭代的旧发布技术快得多。它通过使用Pascal三角形中固有的数学属性来实现。我的论文谈到了这一点。我相信我是第一个发现和发布这种技术的人,但我可能错了。
-
将已排序的二项系数表中的索引转换为相应的K索引。
-
使用 Mark Dominus 方法计算二项式系数,这是多少不太可能溢出并使用更大的数字。
-
该类是用.NET C#编写的,它提供了一种通过使用通用列表来管理与问题相关的对象(如果有)的方法。此类的构造函数采用名为InitTable的bool值,当为true时,将创建一个通用列表来保存要管理的对象。如果此值为false,则不会创建表。不需要创建表来执行上述4种方法。提供访问者方法来访问该表。
-
有一个关联的测试类,它显示了如何使用该类及其方法。它已经过2个案例的广泛测试,并且没有已知的错误。
醇>
要阅读本课程并下载代码,请参阅 Tablizing The Binomial Coeffieicent 。
将此类转换为C ++应该不难。
算法:
- 从1数到2^正。
- 把每个位于其二元表示。
- 翻译每一个"开"的位素的设定,基于的位置。
在C#:
void Main()
{
var set = new [] {"A", "B", "C", "D" }; //, "E", "F", "G", "H", "I", "J" };
var kElement = 2;
for(var i = 1; i < Math.Pow(2, set.Length); i++) {
var result = Convert.ToString(i, 2).PadLeft(set.Length, '0');
var cnt = Regex.Matches(Regex.Escape(result), "1").Count;
if (cnt == kElement) {
for(int j = 0; j < set.Length; j++)
if ( Char.GetNumericValue(result[j]) == 1)
Console.Write(set[j]);
Console.WriteLine();
}
}
}
为什么它的工作原理?
有一个双向注入之间的子集的n元设置和n位序列。
这意味着我们能弄明白如何许多的子集有通过计算序列。
例如,四个元件下可以由{0,1}X{0,1}X{0,1}X{0,1}(或2^4)不同的序列。
所以- 我们所要做的就是从1数到2^n找到所有的组合。 (我们忽略空集。) 接下来,把该数字到他们的二进制代表性。然后替换元件的设置对于"开"的位。
如果你只想要k元结果,只有印当k位'on'。
(如果你想要所有的子集,而不是k长度子集,除cnt/kElement的一部分。)
(证明,请参阅麻省理工学院免费课件的数学计算机科学、雷曼兄弟et al.,部分11.2.2. https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-042j-mathematics-for-computer-science-fall-2010/readings/ )
跳上潮流,并发布另一个解决方案。这是一个通用的Java实现。输入:(int k)
是要选择的元素数,(List<T> list)
是可供选择的列表。返回组合列表(List<List<T>>)
。
public static <T> List<List<T>> getCombinations(int k, List<T> list) {
List<List<T>> combinations = new ArrayList<List<T>>();
if (k == 0) {
combinations.add(new ArrayList<T>());
return combinations;
}
for (int i = 0; i < list.size(); i++) {
T element = list.get(i);
List<T> rest = getSublist(list, i+1);
for (List<T> previous : getCombinations(k-1, rest)) {
previous.add(element);
combinations.add(previous);
}
}
return combinations;
}
public static <T> List<T> getSublist(List<T> list, int i) {
List<T> sublist = new ArrayList<T>();
for (int j = i; j < list.size(); j++) {
sublist.add(list.get(j));
}
return sublist;
}