C
这将对最佳解决方案进行递归搜索,并使用字符串的未知余数可以完成的运行次数上限来对它们进行严重修剪。上限计算使用一个巨大的查找表,该表的大小由常数L
(L=11
:0.5 GiB,L=12
:2 GiB,L=13
:8 GiB)控制。
在我的笔记本电脑上,这会在100秒内上升到n = 50;下一行是142秒。
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define N (8*sizeof(unsigned long long))
#define L 13
static bool a[N], best_a[N];
static int start[N/2 + 1], best_runs;
static uint8_t end_runs[2 << 2*L][2], small[N + 1][2 << 2*L];
static inline unsigned next_state(unsigned state, int b)
{
state *= 2;
state += b;
if (state >= 2 << 2*L) {
state &= ~(2 << 2*L);
state |= 1 << 2*L;
}
return state;
}
static void search(int n, int i, int runs, unsigned state)
{
if (i == n) {
int r = runs;
unsigned long long m = 0;
for (int p = n / 2; p > 0; p--) {
if (i - start[p] >= 2*p && !(m & 1ULL << start[p])) {
m |= 1ULL << start[p];
r++;
}
}
if (r > best_runs) {
best_runs = r;
memcpy(best_a, a, n*sizeof(a[0]));
}
} else {
a[i] = false;
do {
int r = runs, bound = 0, saved = 0, save_p[N/2], save_start[N/2], p, s = next_state(state, a[i]);
unsigned long long m = 0;
for (p = n/2; p > i; p--)
if (p > L)
bound += (n - p + 1)/(p + 1);
for (; p > 0; p--) {
if (a[i] != a[i - p]) {
if (i - start[p] >= 2*p && !(m & 1ULL << start[p])) {
m |= 1ULL << start[p];
r++;
}
save_p[saved] = p;
save_start[saved] = start[p];
saved++;
start[p] = i + 1 - p;
if (p > L)
bound += (n - i)/(p + 1);
} else {
if (p > L)
bound += (n - (start[p] + p - 1 > i - p ? start[p] + p - 1 : i - p))/(p + 1);
}
}
bound += small[n - i - 1][s];
if (r + bound > best_runs)
search(n, i + 1, r, s);
while (saved--)
start[save_p[saved]] = save_start[saved];
} while ((a[i] = !a[i]));
}
}
int main()
{
for (int n = 0; n <= N; n++) {
if (n <= 2*L) {
for (unsigned state = 1U << n; state < 2U << n; state++) {
for (int b = 0; b < 2; b++) {
int r = 0;
unsigned long long m = 0;
for (int p = n / 2; p > 0; p--) {
if ((b ^ state >> (p - 1)) & 1) {
unsigned k = state ^ state >> p;
k &= -k;
k <<= p;
if (!(k & ~(~0U << n)))
k = 1U << n;
if (!((m | ~(~0U << 2*p)) & k)) {
m |= k;
r++;
}
}
}
end_runs[state][b] = r;
}
small[0][state] = end_runs[state][0] + end_runs[state][1];
}
}
for (int l = 2*L < n - 1 ? 2*L : n - 1; l >= 0; l--) {
for (unsigned state = 1U << l; state < 2U << l; state++) {
int r0 = small[n - l - 1][next_state(state, 0)] + end_runs[state][0],
r1 = small[n - l - 1][next_state(state, 1)] + end_runs[state][1];
small[n - l][state] = r0 > r1 ? r0 : r1;
}
}
if (n >= 2) {
search(n, 1, 0, 2U);
printf("%d %d ", n, best_runs);
for (int i = 0; i < n; i++)
printf("%d", best_a[i]);
printf("\n");
fflush(stdout);
best_runs--;
}
}
return 0;
}
输出:
$ gcc -mcmodel=medium -O2 runs.c -o runs
$ ./runs
2 1 00
3 1 000
4 2 0011
5 2 00011
6 3 001001
7 4 0010011
8 5 00110011
9 5 000110011
10 6 0010011001
11 7 00100110011
12 8 001001100100
13 8 0001001100100
14 10 00100110010011
15 10 000100110010011
16 11 0010011001001100
17 12 00100101101001011
18 13 001001100100110011
19 14 0010011001001100100
20 15 00101001011010010100
21 15 000101001011010010100
22 16 0010010100101101001011
23 17 00100101001011010010100
24 18 001001100100110110011011
25 19 0010011001000100110010011
26 20 00101001011010010100101101
27 21 001001010010110100101001011
28 22 0010100101101001010010110100
29 23 00101001011010010100101101011
30 24 001011010010110101101001011010
31 25 0010100101101001010010110100101
32 26 00101001011010010100101101001011
33 27 001010010110100101001011010010100
34 27 0001010010110100101001011010010100
35 28 00100101001011010010100101101001011
36 29 001001010010110100101001011010010100
37 30 0010011001001100100010011001001100100
38 30 00010011001001100100010011001001100100
39 31 001001010010110100101001011010010100100
40 32 0010010100101101001010010110100101001011
41 33 00100110010001001100100110010001001100100
42 35 001010010110100101001011010110100101101011
43 35 0001010010110100101001011010110100101101011
44 36 00101001011001010010110100101001011010010100
45 37 001001010010110100101001011010110100101101011
46 38 0010100101101001010010110100101001011010010100
47 39 00101101001010010110100101101001010010110100101
48 40 001010010110100101001011010010110101101001011010
49 41 0010100101101001010010110100101101001010010110100
50 42 00101001011010010100101101001011010110100101101011
51 43 001010010110100101001011010110100101001011010010100
以下是所有最佳的序列Ñ ≤64(不只是第一字典序),通过该程序和许多小时计算的修改版本生成的。
出色的近乎最佳序列
无穷分形序列的前缀
1010010110100101001011010010110100101001011010010100101…
在变换101↦10100,00↦101下是不变的:
101 00 101 101 00 101 00 101 101 00 101 101 00 …
= 10100 101 10100 10100 101 10100 101 10100 10100 101 10100 10100 101 …
似乎有非常接近最佳的数字内最佳的运行2-总是Ñ ≤64运行在第一数Ñ字符除以Ñ接近(13 - 5√5)/ 2≈0.90983。但是事实证明,这不是最佳比率—请参见注释。