C #, n = 128 ในประมาณ 2:40
using System;
using System.Collections.Generic;
using System.Linq;
namespace Sandbox
{
class PPCG137436
{
public static void Main(string[] args)
{
if (args.Length == 0) args = new string[] { "1", "2", "4", "8", "16", "32", "64", "128" };
foreach (string arg in args)
{
Console.WriteLine(Count(new int[(int)(0.5 + Math.Log(int.Parse(arg)) / Math.Log(2))], 0));
}
}
static int Count(int[] periods, int idx)
{
if (idx == periods.Length)
{
//Console.WriteLine(string.Join(", ", periods));
return 1;
}
int count = 0;
int p = idx == 0 ? 1 : periods[idx - 1];
for (int q = p; q <= 1 << (idx + 1); q++)
{
periods[idx] = q;
if (q == p || q > 1 << idx || p + q - Gcd(p, q) > 1 << idx && UnificationPasses(periods, idx, q)) count += Count(periods, idx + 1);
}
return count;
}
private static int Gcd(int a, int b)
{
while (a > 0) { int tmp = a; a = b % a; b = tmp; }
return b;
}
private static bool UnificationPasses(int[] periods, int idx, int q)
{
UnionSet union = new UnionSet(1 << idx);
for (int i = 0; i <= idx; i++)
{
for (int j = 0; j + periods[i] < Math.Min(2 << i, 1 << idx); j++) union.Unify(j, j + periods[i]);
}
IDictionary<int, long> rev = new Dictionary<int, long>();
for (int k = 0; k < 1 << idx; k++) rev[union.Find(k)] = 0L;
for (int k = 0; k < 1 << idx; k++) rev[union.Find(k)] |= 1L << k;
long zeroes = rev[union.Find(0)]; // wlog the value at position 0 is 0
ISet<int> onesIndex = new HashSet<int>();
// This can be seen as the special case of the next loop where j == -1.
for (int i = 0; i < idx; i++)
{
if (periods[i] == 2 << i) onesIndex.Add((2 << i) - 1);
}
for (int j = 0; j < idx - 1 && periods[j] == 2 << j; j++)
{
for (int i = j + 1; i < idx; i++)
{
if (periods[i] == 2 << i)
{
for (int k = (1 << j) + 1; k <= 2 << j; k++) onesIndex.Add((2 << i) - k);
}
}
}
for (int i = 1; i < idx; i++)
{
if (periods[i] == 1) continue;
int d = (2 << i) - periods[i];
long dmask = (1L << d) - 1;
if (((zeroes >> 1) & (zeroes >> periods[i]) & dmask) == dmask) onesIndex.Add(periods[i] - 1);
}
long ones = 0L;
foreach (var key in onesIndex) ones |= rev[union.Find(key)];
if ((zeroes & ones) != 0) return false; // Definite contradiction!
rev.Remove(union.Find(0));
foreach (var key in onesIndex) rev.Remove(key);
long[] masks = System.Linq.Enumerable.ToArray(rev.Values);
int numFilteredMasks = 0;
long set = 0;
long M = 0;
for (int i = 1; i <= idx; i++)
{
if (periods[i - 1] == 1) continue;
// Sort the relevant masks to the start
if (i == idx) numFilteredMasks = masks.Length; // Minor optimisation: skip the filter because we know we need all the masks
long filter = (1L << (1 << i)) - 1;
for (int j = numFilteredMasks; j < masks.Length; j++)
{
if ((masks[j] & filter) != 0)
{
var tmp = masks[j];
masks[j] = masks[numFilteredMasks];
masks[numFilteredMasks++] = tmp;
}
}
// Search for a successful assignment, using the information from the previous search to skip a few initial values in this one.
set |= (1L << numFilteredMasks) - 1 - M;
M = (1L << numFilteredMasks) - 1;
while (true)
{
if (TestAssignment(periods, i, ones, masks, set)) break;
if (set == 0) return false; // No suitable assignment found
// Gosper's hack with variant to reduce the number of bits on overflow
long c = set & -set;
long r = set + c;
set = (((r ^ set) >> 2) / c) | (r & M);
}
}
return true;
}
private static bool TestAssignment(int[] periods, int idx, long ones, long[] masks, long assignment)
{
for (int j = 0; j < masks.Length; j++, assignment >>= 1) ones |= masks[j] & -(assignment & 1);
for (int i = idx - 1; i > 0; i--) // i == 0 is already handled in the unification process.
{
if (Period(ones, 2 << i, periods[i - 1]) < periods[i]) return false;
}
return true;
}
private static int Period(long arr, int n, int min)
{
for (int p = min; p <= n; p++)
{
// If the bottom n bits have period p then the bottom (n-p) bits equal the bottom (n-p) bits of the integer shifted right p
long mask = (1L << (n - p)) - 1L;
if ((arr & mask) == ((arr >> p) & mask)) return p;
}
throw new Exception("Unreachable");
}
class UnionSet
{
private int[] _Lookup;
public UnionSet(int size)
{
_Lookup = new int[size];
for (int k = 0; k < size; k++) _Lookup[k] = k;
}
public int Find(int key)
{
var l = _Lookup[key];
if (l != key) _Lookup[key] = l = Find(l);
return l;
}
public void Unify(int key1, int key2)
{
int root1 = Find(key1);
int root2 = Find(key2);
if (root1 < root2) _Lookup[root2] = root1;
else _Lookup[root1] = root2;
}
}
}
}
การขยายไปยัง n = 256 จะต้องเปลี่ยนไปBigInteger
ใช้มาสก์ซึ่งอาจฆ่าประสิทธิภาพมากเกินไปสำหรับ n = 128 ที่จะผ่านไปโดยไม่มีแนวคิดใหม่ให้อยู่คนเดียว n = 256
ภายใต้ Linux, รวบรวมและดำเนินการกับmono-csc
mono
คำอธิบายพื้นฐาน
ฉันจะไม่แยกส่วนทีละบรรทัดเพียงภาพรวมของแนวคิด
ตามกฎของหัวแม่มือฉันยินดีที่จะย้ำผ่านตามลำดับของ 2 50องค์ประกอบในโปรแกรม combinatoric เดรัจฉานบังคับ ในการไปถึง n = 128 ดังนั้นจึงจำเป็นต้องใช้วิธีการที่ไม่ได้วิเคราะห์ทุก bitstring ดังนั้นแทนที่จะใช้การส่งต่อจากสตริงบิตไปยังลำดับรอบระยะเวลาฉันทำงานย้อนหลัง: กำหนดลำดับของรอบเวลามีบิตสตริงที่รู้หรือไม่ สำหรับ n = 2 xมีขอบเขตบนที่ง่ายของ 2 x (x + 1) / 2 จุดลำดับ (vs 2 2 x bitstrings)
บางข้อโต้แย้งใช้คำศัพท์เฉพาะของสตริง :
ให้p
และจะมีสองช่วงเวลาของสตริงของความยาวq
n
หากp + q ≤ n + gcd(p, q)
แล้วgcd(p, q)
ยังเป็นช่วงเวลาของการสตริง
WLOG ผมจะสมมติว่าทุก bitstrings 0
ภายใต้การพิจารณาการเริ่มต้นกับ
เมื่อกำหนดลำดับของรอบระยะเวลาซึ่งเป็นช่วงเวลาของส่วนนำหน้าของความยาว 2 i ( เสมอ) จะมีข้อสังเกตง่ายๆเกี่ยวกับค่าที่เป็นไปได้ของ:[p1 p2 ... pk]
pi
p0 = 1
pk+1
pk+1 ≥ pk
ตั้งแต่สมัยของสตริงยังเป็นช่วงเวลาของการคำนำหน้าใดS
ๆS
pk+1 = pk
เป็นส่วนขยายที่เป็นไปได้เสมอ: เพียงทำซ้ำสตริงดั้งเดิมดั้งเดิมสองเท่าของอักขระจำนวนมาก
2k < pk+1 ≤ 2k+1
เป็นนามสกุลที่เป็นไปได้เสมอ มันพอเพียงที่จะแสดงสิ่งนี้เพราะสตริงความยาว aperiodic สามารถขยายเป็นสตริงความยาว aperiodic ได้โดยการเพิ่มตัวอักษรใด ๆ ที่ไม่ใช่ตัวอักษรตัวแรกpk+1 = 2k+1
L
L+1
ใช้สตริงSx
ที่มีความยาว 2 kซึ่งมีระยะเวลาและพิจารณาสตริงที่มีความยาว 2 + 1 k เห็นได้ชัดว่ามีระยะเวลา 2 k +1 สมมติว่าช่วงเวลานั้นเล็กลงpk
SxyS
SxyS
q
จากนั้นตามช่วงเวลาบทแทรกก็เป็นช่วงเวลาและเนื่องจากตัวหารที่ยิ่งใหญ่ที่สุดมีค่าน้อยกว่าหรือเท่ากับอาร์กิวเมนต์และเป็นช่วงเวลาที่เล็กที่สุดเราจึงจำเป็นต้องเป็นปัจจัยที่เหมาะสมของ 2 k +1 เนื่องจากความฉลาดของมันไม่สามารถเป็นที่ 2 เรามี2k+1 + q ≤ 2k+1+1 ≤ 2k+1 + gcd(2k+1, q)
gcd(2k+1, q)
SxyS
q
q
q ≤ (2k+1)/3
ตอนนี้ตั้งแต่เป็นช่วงเวลาของมันจะต้องเป็นช่วงเวลาของ แต่ระยะเวลาของการมี เรามีสองกรณี:q ≤ 2k
SxyS
Sx
Sx
pk
gcd(pk, q) = pk
หรือแบ่งให้เท่ากันทั้งหมดpk
q
pk + q > 2k + gcd(pk, q)
ดังนั้นการแทรกช่วงเวลาไม่ได้บังคับช่วงเวลาที่สั้นลง
พิจารณากรณีแรกก่อน , ขัดแย้งกับความหมายของระยะเวลาของ ดังนั้นเราจึงจะถูกบังคับให้ข้อสรุปที่เป็นปัจจัยของpk > 2k + gcd(pk, q) - q ≥ 2k+1 - q ≥ 2k+1 - (2k+1)/3 ≥ 2q
pk
Sx
pk
q
แต่เนื่องจากq
เป็นช่วงเวลาSx
และเป็นช่วงเวลาคำนำหน้าของความยาวจึงเป็นเพียงสำเนาของคำนำหน้าความยาวดังนั้นเราจึงเห็นว่าเป็นช่วงเวลาด้วยpk
Sx
q
q/pk
pk
pk
SxyS
ดังนั้นช่วงเวลาSxyS
คือหรือ 2 k +1 แต่เรามีสองทางเลือก! อย่างน้อยหนึ่งตัวเลือกจะให้ช่วงเวลาดังนั้นอย่างน้อยหนึ่งตัวเลือกจะให้ช่วงเวลา 2 k +1 QEDpk
y
y
pk
บทแทรกช่วงเวลาช่วยให้เราสามารถปฏิเสธส่วนขยายที่เหลืออยู่บางส่วนได้
ส่วนขยายใด ๆ ที่ไม่ผ่านการยอมรับอย่างรวดเร็วหรือการทดสอบแบบปฏิเสธด่วนจะต้องทำการทดสอบอย่างสร้างสรรค์
การสร้าง bitstring ที่กำหนดลำดับของช่วงเวลานั้นเป็นปัญหาที่น่าพอใจ แต่ก็มีโครงสร้างจำนวนมาก มีข้อ จำกัด ด้านความเท่าเทียมกันอย่างง่าย ๆ ที่บอกเป็นนัยในแต่ละช่วงเวลาของคำนำหน้าดังนั้นฉันจึงใช้โครงสร้างข้อมูลแบบชุดยูเนี่ยนเพื่อรวมบิตเป็นกลุ่มอิสระ นี่เพียงพอที่จะจัดการกับ n = 64 แต่สำหรับ n = 128 มันจำเป็นต้องดำเนินการต่อไป ฉันใช้อาร์กิวเมนต์ที่มีประโยชน์สองบรรทัด:2k - pk
- ถ้าคำนำหน้าของความยาว
M
เป็นและคำนำหน้าของความยาวที่มีระยะเวลาแล้วคำนำหน้าของความยาวต้องลงท้ายด้วย นี่เป็นสิ่งที่ทรงพลังที่สุดในกรณีที่จะมีกลุ่มอิสระที่สะดวกที่สุด01M-1
L > M
L
L
1M
- ถ้าคำนำหน้าของความยาว
M
เป็นและคำนำหน้าของความยาวที่มีระยะเวลาที่มีและสิ้นสุดในนั้นจะต้องอยู่ในความเป็นจริงในตอนท้าย นี่เป็นสิ่งที่ทรงพลังที่สุดในสิ่งที่ตรงกันข้ามมากที่สุดเมื่อลำดับรอบระยะเวลาเริ่มต้นด้วยจำนวนมาก0M
L > M
L - d
d < M
0d
10d
หากเราไม่ได้รับความขัดแย้งในทันทีโดยบังคับให้คลัสเตอร์ที่มีบิตแรก (สันนิษฐานว่าเป็นศูนย์) เป็นหนึ่งแล้วเราจะเดรัจฉานแรง (กับ micro-optimisations บาง) มากกว่าค่าที่เป็นไปได้สำหรับกลุ่มไม่ได้บังคับ ทราบว่าสั่งซื้อจากมากไปน้อยจำนวนคนเพราะถ้าi
THบิตเป็นหนึ่งจากนั้นช่วงเวลาที่ไม่สามารถi
และเราอยากจะหลีกเลี่ยงช่วงเวลาที่สั้นกว่าคนที่มีการบังคับใช้แล้วโดยการจัดกลุ่ม การลงไปเพิ่มโอกาสในการค้นหาการบ้านที่ถูกต้อง แต่เนิ่นๆ