Algorithm 最小变化算法,最大化';交换';
这是一个非数学家提出的关于组合数学的问题,所以请尽量容忍我 给定一个由n个不同字符组成的数组,我希望以最小的更改顺序生成k个字符的子集,即,第I+1代仅包含一个不在第I代中的字符的顺序。这本身并不难。但是,我还想最大限度地增加在第I+1代中交换的角色与在第I代中交换的角色相同的情况。举例说明,对于n=7,k=3: abc abd abe*abf*abg*afg aeg*adg*acg*acd ace*acf*aef adf*ade bde bdf bef bcf*bce bcd*bcg*bdg beg*bfg*cfg ceg*cdg*cde cdf*cef def deg dfg efg 带星号的字符串表示我希望最大化的情况;e、 g.第3代中新出现的e(abe)取代了第2代中新出现的d(abd)。这似乎不可能在每一代人中都发生,但我希望它尽可能经常发生 我使用的典型数组大小为20-30,子集大小约为5-8 我使用的是一种奇怪的语言(或者实际上是它的派生语言),所以我不希望任何人发布我可以直接使用的代码。但我会很感激伪代码中的答案或提示,并会尽我所能翻译C等。此外,我注意到,这类问题经常以整数数组的形式进行讨论,我当然可以将以这种方式发布的解决方案应用于我自己的问题 谢谢 金巴斯汀 2010年6月15日编辑: 我似乎确实陷入了比我想象的更深的水里,虽然我很感激所有的答案,但并不是所有的答案都是相关的。作为一个不充分解决方案的示例,让我发布我自己的Unicon过程,以最小的变更顺序生成字符集s的k元子集。理解代码需要知道的是:前置*表示结构的大小,因此如果s是字符串,*s表示s的大小(它包含的字符数)。|是一个字符串连接操作。一个介词!依次在连续过程中生成结构的每个元素,例如字符串的每个字符。“suspend”控制结构从一个过程返回一个结果,但在所有局部变量就位的情况下使该过程处于“suspend”状态,因此,如果在循环中调用该过程,则可以生成新的结果Algorithm 最小变化算法,最大化';交换';,algorithm,combinatorics,Algorithm,Combinatorics,这是一个非数学家提出的关于组合数学的问题,所以请尽量容忍我 给定一个由n个不同字符组成的数组,我希望以最小的更改顺序生成k个字符的子集,即,第I+1代仅包含一个不在第I代中的字符的顺序。这本身并不难。但是,我还想最大限度地增加在第I+1代中交换的角色与在第I代中交换的角色相同的情况。举例说明,对于n=7,k=3: abc abd abe*abf*abg*afg aeg*adg*acg*acd ace*acf*aef adf*ade bde bdf bef bcf*bce bcd*bcg*bdg
procedure revdoor(s, k)
# Produces all k-subsets of a string or character set s in a 'revolving
# door' order. Each column except the first traverses the characters
# available to it in alphabetical and reverse alphabetical order
# alternately. The order of the input string is preserved.
# If called in a loop as revdoor("abcdefg", 3),
# the order of production is: abc, abd, abe, abf, abg, acg, acf, ace, acd,
# ade, adf, adg, aeg, aef, afg, bfg, bef, beg, bdg, bdf, bde, bcd, bce,
# bcf, bcg, cdg, cdf, cde, cef, ceg, cfg, dfg, deg, def, efg
local i
static Ctl
if /Ctl then { # this means 'if Ctl doesn't exist'
if k = 0 then return ""
Ctl := list(k, 1) # a list of k elements, each initialised to 1.
}
if Ctl[k] = 1 then {
if k = 1 then suspend !s else
every i := 1 to *s-k+1 do {
suspend s[i] || revdoor(s[i+1:0], k-1)
}
} else {
if k = 1 then suspend !reverse(s) else
every i := -k to -*s by -1 do {
suspend s[i] || revdoor(s[i+1:0], k-1)
}
}
# the following line multiplies element k of Ctl by -1 if k < size of Ctl
# (this controls the order of generation of characters),
# and destroys Ctl on final exit from the procedure.
if k < *Ctl then Ctl[k] *:= -1 else Ctl := &null
end
程序旋转门(s、k)
#生成“循环”中字符串或字符集的所有k子集
#门令。除第一列外的每一列都遍历字符
#按字母顺序和逆字母顺序提供
#交替地。将保留输入字符串的顺序。
#如果在循环中被称为revdoor(“abcdefg”,3),
#生产顺序为:abc、abd、abe、abf、abg、acg、acf、ace、acd、,
#ade、adf、adg、aeg、aef、afg、bfg、bef、beg、bdg、bdf、bde、bcd、bce、,
#bcf、bcg、cdg、cdf、cde、cef、ceg、cfg、dfg、deg、def、efg
本地i
静态Ctl
if/Ctl then{#这意味着“如果Ctl不存在”
如果k=0,则返回“”
Ctl:=列表(k,1)#k个元素的列表,每个元素初始化为1。
}
如果Ctl[k]=1,则{
如果k=1,则挂起!s else
每i:=1到*s-k+1 do{
暂停s[i]| |旋转门(s[i+1:0],k-1)
}
}否则{
如果k=1,则挂起!反之亦然
每i:=-k到-*s乘以-1{
暂停s[i]| |旋转门(s[i+1:0],k-1)
}
}
#如果k
请注意,在我看来,上述过程的输出不是最优的。到目前为止,我调查的一个结果是,n个元素的k元子集列表的最大“交换分数”不小于comb(n-1,k),或者在n=7,k=3的情况下,最大分数至少为comb(6,3)=20。我将列表的“交换分数”定义为列表中的项目数,该项目的新元素替换了前一项目中的一个元素,该项目本身是新的。我没有数学设备来证明这一点,但用k=1或k=2很容易看出。对于某些(n,k),可能会获得略高的分数,如n=7,k=3:
abc abd abe abf abgacg adg aeg afg
efg dfg cfg bfg
beg bdg bcg
bcd bce bcf
bdf bef
def cef aef
adf acf
acd ace
ade
bde cde
cdf cdg
ceg
度(交换分数=21) 可能需要注意的是,上面的列表处于“强最小变更顺序”(如单词golf:新角色始终与它替换的角色处于相同的位置),这可能表明我自己的工作方向。我希望在几天内发布更多内容
金这相当简单。为了最大化替换,只需将字符视为数字,并将字符串增加1,直到达到上限。 然后检查是否在字符串中两次使用相同的字符。 我认为这是可行的:
char c[] = {'a', 'b', 'c', 'd', 'e'};
const int n = 5;
const int k = 3;
char s[k];
void print()
{
for( int i = 0; i < k; ++i )
putchar(c[s[i]]);
putchar('\n');
}
bool increment( int m )
{
// reached the limit?
if( ++s[m] == n && m == 0 )
return false;
next:
for( int i = 0; i < m; ++i )
{
if( s[m] == n )
{
// carry
s[m] = 0;
if( !increment( m-1 ))
return false;
goto next;
}
else if( s[i] == s[m] )
{
// the character is already used
++s[m];
goto next;
}
}
return true;
}
int main(int, char**)
{
// initialise
for( int i = 0; i < k; ++i )
s[i] = i;
// enumerate all combinations
do
print();
while(increment(k-1));
}
charc[]={'a','b','c','d','e'};
常数int n=5;
常数int k=3;
chars[k];
作废打印()
{
对于(int i=0;ipublic class Homework
{
/**
* Prints all k-combinations of a set of n elements. Answer to this
* question: http://stackoverflow.com/questions/2698551
*/
public static void main(String[] args)
{
Combinations combinations = new Combinations(7, 3);
System.out.printf(
"Printing all %d %d-combinations of a set with %d elements:\n",
combinations.size(), combinations.k, combinations.n);
for (int[] c : combinations)
System.out.println(Arrays.toString(c));
}
/**
* Provides an iterator for all k-combinations of a set of n elements.
*/
static class Combinations implements Iterable<int[]>
{
public final int n, k;
public Combinations(int n, int k)
{
if (n < 1 || n < k)
throw new IllegalArgumentException();
this.n = n;
this.k = k;
}
@Override
public Iterator<int[]> iterator()
{
return new Iterator<int[]>()
{
private int[] c;
@Override
public void remove() { throw new UnsupportedOperationException(); }
@Override
public int[] next()
{
if (c == null)
{
c = new int[k];
for (int i = 0; i < k; i++)
c[i] = i;
}
else
{
int i = c.length - 1;
while (i >= 0 && c[i] == n - k + i)
i--;
if (i < 0)
throw new NoSuchElementException();
c[i]++;
for (int j = i + 1; j < c.length; j++)
c[j] = c[i] + j - i;
}
return c.clone(); // remove defensive copy if performance is more important
}
@Override
public boolean hasNext() { return c == null || c[0] < n - k; }
};
}
/**
* Returns number of combinations: n! / (k! * (n - k)!).
*/
public BigInteger size()
{
BigInteger s = BigInteger.valueOf(n);
for (int i = n - 1; i > n - k; i--)
s = s.multiply(BigInteger.valueOf(i));
for (int i = k; i > 1; i--)
s = s.divide(BigInteger.valueOf(i));
return s;
}
}
}
Printing all 35 3-combinations of a set with 7 elements:
[0, 1, 2] [0, 1, 3] [0, 1, 4] [0, 1, 5] [0, 1, 6] [0, 2, 3] [0, 2, 4] [0, 2, 5] [0, 2, 6] [0, 3, 4]
[0, 3, 5] [0, 3, 6] [0, 4, 5] [0, 4, 6] [0, 5, 6] [1, 2, 3] [1, 2, 4] [1, 2, 5] [1, 2, 6] [1, 3, 4]
[1, 3, 5] [1, 3, 6] [1, 4, 5] [1, 4, 6] [1, 5, 6] [2, 3, 4] [2, 3, 5] [2, 3, 6] [2, 4, 5] [2, 4, 6]
[2, 5, 6] [3, 4, 5] [3, 4, 6] [3, 5, 6] [4, 5, 6]
Combination nextCombo();
vector<Combination> allCombinations();
1 2 3
*a
b *b
c c *c
d d *d
e e *e
*f *f
*g
Col1 Col2
---- ----------------------------
a ab ac ad ae af ag
b ab bc bd be bf bg
c ac bc cd ce cf cg
d ad bd cd de df dg
e ae be ce de ef eg
f af bf cf df ef fg
g ag bg cg dg eg fg
a ab ac ad ae af ag
b bc bd be bf bg
c cd ce cf cg
d de df dg
e ef eg
f fg
g
a !ab ac ad ae af ag
b !bc bd be bf bg
c !cd ce cf cg
d !de df dg
e !ef eg
f !fg
g
a !ab ac ad ae af ag
b !bc bd be bf bg
c !cd ce cf cg
d !de df dg
e !ef eg
f !fg
a !ab ac! ad ae af ag
b !bc bd! be bf bg
c !cd ce! cf cg
d !de df! dg
e !ef eg!
f !fg
Ordered rows:
a !ab ac! ad ae af ag ab ag af ae ad ac
b !bc bd! be bf bg bc bg bf be bd
c !cd ce! cf cg cd cg cf ce
d !de df! dg de dg df
e !ef eg! ef eg
f !fg fg
Ordered rows:
ab !abc abd abe abf abg! abc abd abe abf abg
ag acg adg aeg! !afg afg acg adg aeg
af acf adf! !aef aef acf adf
ae ace! !ade ade ace
ad !acd! acd
ac
bc !bcd bce bcf bcg! bcd bce bcf bcg
bg bdg beg! !bfg bfg bdg beg
bf bdf! !bef bef bdf
be !bde! bde
bd
cd !cde cdf cdg! cde cdf cdg
cg ceg! !cfg cfg ceg
cf !cef! cef
ce
de !def deg! def deg
dg !dfg! dfg
df
ef !efg efg
eg
fg
Ordered rows:
abc !abcd abce! abcf abcg abcd abcg abcf abce
abd !abde abdf! abdg abde abdg abdf
abe !abef abeg! abef abeg
abf !abfg! abfg
abg
afg acfg! adfg !aefg aefg adfg acfg
acg !acdg aceg! acdg aceg
adg !adeg! adeg
aeg
aef acef! !adef adef acef
acf !acdf! acdf
adf
ade !acde! acde
ace
acd
bcd !bcde bcdf! bcdg bcde bcdg bcdf
bce !bcef bceg! bcef bceg
bcf !bcfg! bcfg
bcg
bfg bdfg! !befg befg bdfg
bdg !bdeg! bdeg
beg
bef !bdef! bdef
bdf
bde
cde !cdef cdeg! cdef cdeg
cdf !cdfg! cdfg
cdg
cfg !cefg! cefg
ceg
cef
def !defg defg
deg
dfg
efg
Ordered rows:
abcd !abcde abcdf abcdg! abcde abcdf abcdg
abcg abceg! !abcfg abcfg abceg
abcf !abcef! abcef
abce
abde !abdef abdeg! abdef abdeg
abdg !abdfg! abdfg
abdf
abef !abefg! abefg
abeg
abfg
aefg acefg! !adefg adefg acefg
adfg !acdfg! acdfg
acfg
acdg !acdeg! acdeg
aceg
adeg
adef !acdef! acdef
acef
acdf
acde
bcde !bcdef bcdeg! bcdef bcdeg
bcdg !bcdfg! bcdfg
bcdf
bcef !bcefg! bcefg
bceg
bcfg
befg !bdefg! bdefg
bdfg
bdeg
bdef
cdef !cdefg cdefg
cdeg
cdfg
cefg
defg
Ordered rows:
abcde !abcdef abcdeg! abcdef abcdeg
abcdf !abcdfg! abcdfg
abcdg
abcfg !abcefg! abcefg
abceg
abcef
abdef !abdefg! abdefg
abdeg
abdfg
abefg
adefg
acefg !acdefg! acdefg
acdfg
acdeg
acdef
bcdef !bcdefg bcdefg
bcdeg
bcdfg
bcefg
bdefg
cdefg
procedure maxlifo(s:string, k:integer)
# A solution to my combinatorics problem from 2010.
# Return a list of the k subsets of the characters of a string s
# in a minimal change order such that last-in first-out is maximised.
# String s must not contain duplicate characters and in the present
# implementation must not contain "!", which is used as a marker.
local ch, cand, Hit, inps, i, j, K, L, Outp, R, S
# Errors
if *cset(s) ~= *s then
stop("Duplicate characters in set in maxlifo(", s, ", ", k, ")")
if find("!", s) then
stop("Illegal character in set in maxlifo(", s, ", ", k, ")")
if k > *s then
stop("Subset size larger than set size in maxlifo(", s, ", ", k, ")")
# Special cases
if k = 0 then return []
if k = *s then return [s]
Outp := []
if k = 1 then {
every put(Outp, !s)
return Outp
}
# Default case
S := set()
K := []
# Build cliques from output of maxlifo(s, k-1) with the remaining
# characters in s, substituting empty strings as placeholders for
# subsets already listed.
every inps := !maxlifo(s, k-1) do {
R := []
every ch := !s do
if not find(ch, inps) then {
cand := reorder(inps ++ ch, s)
if member(S, cand) then cand := "" else insert(S, cand)
put(R, cand)
}
put(K, R)
}
# Mark ‘first’ subset in each row with initial "!"
every i := 1 to *K - 1 do {
every j := 1 to *K[i] do
if K[i, j] ~== "" & K[i+1, j] == "" then {
K[i, j] := "!" || K[i, j]
break
}
}
# Remove rows containing only placeholders
every i := *K to 1 by -1 do {
every if !K[i] ~== "" then break next
delete(K, i)
}
# Mark ‘last’ subset in each row with final "!"
every i := 1 to *K - 1 do
every j := 1 to *K[i] do
if K[i+1, j][1] == "!" then {
K[i, j] ||:= "!"
break
}
# Build output list
every R := !K do {
# Delete placeholders from row (no longer needed and in the way)
every j := *R to 1 by -1 do if R[j] == "" then delete(R, j)
# Handle ‘first’ subset and remove from row
# N.B. ‘First’ subset will be leftmost or rightmost in row
if R[1][1] == "!" then
put(Outp, trim(get(R), '!', 0))
else put(Outp, trim(pull(R), '!', 0))
# Handle any remaining subsets, ‘last’ subset last, stripping '!' markers
# N.B. ‘Last’ subset will be leftmost or rightmost in row after removal
# of ‘first’ subset.
if R[-1][-1] == "!" then while put(Outp, trim(get(R), '!', 0)) else
while put(Outp, trim(pull(R), '!', 0))
}
return Outp
end
procedure reorder(cs:cset, s:string)
# Reorder cset cs according to string s
local r
# If no s, return set in alphabetical order
if /s then return string(cs)
r := ""
s ? while tab(upto(cs)) do r ||:= move(1)
return r
end