C #, n = 128, yaklaşık 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 değerine genişletmek, n = BigInteger
128'in performansı n = 256 olsun, yeni fikirler olmadan geçmesi için muhtemelen çok fazla performansı öldüren maskeler için geçiş yapılmasını gerektirir .
Linux altında, ile derleyin mono-csc
ve çalıştırın mono
.
Temel açıklama
Ben satır satır diseksiyon yapmayacağım, sadece kavramlara genel bir bakış.
Genel bir kural olarak, kaba kuvvet kombinatorik bir programda 2 50 elemanın sırasını yinelemekten mutluluk duyuyorum . Bu nedenle n = 128'e ulaşmak için her bit dizisini analiz etmeyen bir yaklaşım kullanmak gerekir. Yani bit dizelerinden dönem dizilerine doğru ilerlemek yerine, geriye doğru çalışıyorum: bir dönem dizisi verildiğinde, bunu gerçekleştiren bir bit dizisi var mı? N = 2 x için , 2 x (x + 1) / 2 periyot dizisinin (2 2 x bit dizisine kıyasla ) kolay bir üst sınırı vardır .
Bazı argümanlar dize periyodiklik lemmasını kullanır :
Izin vermek p
ve q
uzunluk dizesinin iki periyodu olsun n
. Eğer p + q ≤ n + gcd(p, q)
öyleyse gcd(p, q)
, dizenin bir dönemidir.
Wlog Düşünülen tüm bit dizgilerinin ile başladığını varsayacağım 0
.
2 i uzunluğunun önekinin periyodu ( her zaman) olan bir dönem sırası verildiğinde , aşağıdakilerin olası değerleri hakkında bazı kolay gözlemler vardır :[p1 p2 ... pk]
pi
p0 = 1
pk+1
pk+1 ≥ pk
çünkü bir dizginin S
periyodu da herhangi bir önekinin periyodudur S
.
pk+1 = pk
her zaman olası bir uzantıdır: sadece aynı ilkel dizeyi iki kat daha fazla karakter için tekrarlayın.
2k < pk+1 ≤ 2k+1
her zaman olası bir uzantıdır. Bunun gösterilmesi yeterlidir, çünkü bir aperiodic uzunluk dizisi bir aperiodic uzunluk dizisine uzatılabilirpk+1 = 2k+1
L
L+1
, ilk harfi olmayan herhangi bir harf eklenerek aperiodik bir .
Sx
Süresi 2 k olan bir dize alın ve 2 k + 1 uzunluğunda olan dizeyi düşünün . Açıkça sahip bir dönem 2 k + 1. Diyelim ki süresi daha küçük.pk
SxyS
SxyS
q
Öyleyse periyodiklik ile lemma da bir dönemdir ve en büyük bölen argümanlarına eşit veya daha küçük olduğu ve en küçük dönem olduğu için , 2 k + 1'lik uygun bir faktör olmamız gerekir . Bölüm 2 olamaz, biz var .2k+1 + q ≤ 2k+1+1 ≤ 2k+1 + gcd(2k+1, q)
gcd(2k+1, q)
SxyS
q
q
q ≤ (2k+1)/3
Şimdi bir dönem olduğu için bir dönem olmalı . Ama dönemi olduğunu . İki vakamız var:q ≤ 2k
SxyS
Sx
Sx
pk
gcd(pk, q) = pk
veya eşdeğer olarak tam olarak bölünür .pk
q
pk + q > 2k + gcd(pk, q)
böylece periyodik lemma daha küçük bir dönemi zorlamaz.
İlk olarak ikinci vakayı düşünün. , dönemi olarak tanımlanması ile çelişiyor . Bu nedenle , bir faktör olan sonuca zorlanıyoruz .pk > 2k + gcd(pk, q) - q ≥ 2k+1 - q ≥ 2k+1 - (2k+1)/3 ≥ 2q
pk
Sx
pk
q
.
Fakat q
bir dönemdir Sx
ve süresidir , uzunluğunun önek adildir uzunluğunun önek kopyaları biz görüyoruz, böylece de bir dönemdir .pk
Sx
q
q/pk
pk
pk
SxyS
Bu nedenle süresi SxyS
ya bir ya da 2 k + 1. Ama iki seçeneğimiz var ! En fazla bir seçim dönemi verir , bu yüzden en az biri dönem 2 k +1 verir. QED.pk
y
y
pk
Periyodik lemma, kalan olası uzantıların bazılarını reddetmemize izin verir.
Hızlı kabul veya hızlı reddetme testini geçemeyen uzantıların yapıcı olarak test edilmesi gerekir.
Bir periyot dizisi verilen bir bit dizisinin oluşturulması esasen bir tatmin edilebilirlik problemidir, ancak çok fazla yapıya sahiptir. Her önek periyodunun ima ettiği basit eşitlik kısıtlamaları vardır , bu yüzden bitleri bağımsız kümeler halinde birleştirmek için bir birleşim kümesi veri yapısı kullanırım. Bu, n = 64 ile başa çıkmak için yeterliydi, ancak n = 128 için daha ileri gitmek gerekiyordu. İki yararlı tartışma çizgisi kullanıyorum:2k - pk
- Uzunluğunun öneki ise
M
olduğu ve boyunun önek süreye sahip sonra uzunluğunun önek içinde sona ermelidir . Bu, aksi takdirde en bağımsız kümeleri en uygun olan durumlarda en güçlü olanıdır.01M-1
L > M
L
L
1M
- Uzunluğunun öneki ise
M
olduğu ve boyunun önek süreye sahip olan ve uçları o zaman gerçeği sonunda bir zorunluluktur . Bu, dönem sekansı çok sayıda ile başladığında, en uçta en güçlü olanıdır.0M
L > M
L - d
d < M
0d
10d
Kümeyi ilk bitle (sıfır olduğu varsayılır) bir olacak şekilde zorlayarak derhal bir çelişki almazsak, kuvvetlenmemiş kümeler için olası değerler üzerinde kuvvet (bazı mikro optimizasyonlar ile) şiddetleniriz. Siparişin azalan sayıda olduğuna dikkat edin, çünkü i
th bit bir ise, dönem olamaz i
ve kümelenme tarafından zaten zorlananlardan daha kısa sürelerden kaçınmak isteriz. Aşağı inmek, geçerli bir ödevi erken bulma şansını artırır.