24/13 26/14 28/15 30/16 32/17 (C #)
Düzenle:
Cevabımdaki eski bilgileri sildi. Ben kendi optimizasyon bazı ekledim rağmen, Peter Taylor ( Düzenleme: şimdi daha iyi bir algoritma kullanıyor gibi görünüyor) çoğunlukla aynı algoritmayı kullanıyorum:
- Aynı vektör toplamına sahip sütun kümelerini arama stratejisi "ortada buluş" stratejisini uyguladım ( bu KennyTM'nin yorumu tarafından önerildi ). Bu strateji, bellek kullanımını çok geliştirdi, ancak oldukça yavaş, bu yüzden
HasPropertyXFast
"ortada buluşma" yaklaşımını kullanmadan önce eşit miktarda küçük set olup olmadığını hızlıca kontrol eden işlevi ekledim .
- İşlevdeki sütun kümeleri arasında yineleme yaparken
HasPropertyXFast
, sütun kümelerini 1 sütun, sonra 2, 3 vb. Sütun toplamlarının ilk çarpışması bulunur bulunmaz işlev geri döner. Pratikte bu, milyonlarca değil, yalnızca birkaç yüz veya binlerce sütun kümesini kontrol etmem gerektiği anlamına gelir.
long
Tüm sütunları ve vektör toplamlarını depolamak ve karşılaştırmak için değişkenler kullanıyorum . Bu yaklaşım, sütunları diziler olarak karşılaştırmaya göre en azından daha büyük bir mertebedir.
long
Veri türü ve kullanım kalıplarım için optimize edilmiş kendi hashset uygulamasını ekledim .
- Bellek ayırma sayısını azaltmak ve performansı artırmak için uygulamanın tüm ömrü boyunca aynı 3 karma setleri yeniden kullanıyorum.
- Çoklu kullanım desteği.
Program çıktısı:
00000000000111011101010010011111
10000000000011101110101001001111
11000000000001110111010100100111
11100000000000111011101010010011
11110000000000011101110101001001
11111000000000001110111010100100
01111100000000000111011101010010
00111110000000000011101110101001
10011111000000000001110111010100
01001111100000000000111011101010
00100111110000000000011101110101
10010011111000000000001110111010
01001001111100000000000111011101
10100100111110000000000011101110
01010010011111000000000001110111
10101001001111100000000000111011
11010100100111110000000000011101
Score: 32/17 = 1,88235294117647
Time elapsed: 02:11:05.9791250
Kod:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
class Program
{
const int MaxWidth = 32;
const int MaxHeight = 17;
static object _lyndonWordLock = new object();
static void Main(string[] args)
{
Stopwatch sw = Stopwatch.StartNew();
double maxScore = 0;
const int minHeight = 17; // 1
for (int height = minHeight; height <= MaxHeight; height++)
{
Console.WriteLine("Row count = " + height);
Console.WriteLine("Time elapsed: " + sw.Elapsed + "\r\n");
int minWidth = Math.Max(height, (int)(height * maxScore) + 1);
for (int width = minWidth; width <= MaxWidth; width++)
{
#if MULTITHREADING
int[,] matrix = FindMatrixParallel(width, height);
#else
int[,] matrix = FindMatrix(width, height);
#endif
if (matrix != null)
{
PrintMatrix(matrix);
Console.WriteLine("Time elapsed: " + sw.Elapsed + "\r\n");
maxScore = (double)width / height;
}
else
break;
}
}
}
#if MULTITHREADING
static int[,] FindMatrixParallel(int width, int height)
{
_lyndonWord = 0;
_stopSearch = false;
int threadCount = Environment.ProcessorCount;
Task<int[,]>[] tasks = new Task<int[,]>[threadCount];
for (int i = 0; i < threadCount; i++)
tasks[i] = Task<int[,]>.Run(() => FindMatrix(width, height));
int index = Task.WaitAny(tasks);
if (tasks[index].Result != null)
_stopSearch = true;
Task.WaitAll(tasks);
foreach (Task<int[,]> task in tasks)
if (task.Result != null)
return task.Result;
return null;
}
static volatile bool _stopSearch;
#endif
static int[,] FindMatrix(int width, int height)
{
#if MULTITHREADING
_columnSums = new LongSet();
_left = new LongSet();
_right = new LongSet();
#endif
foreach (long rowTemplate in GetLyndonWords(width))
{
int[,] matrix = new int[width, height];
for (int x = 0; x < width; x++)
{
int cellValue = (int)(rowTemplate >> (width - 1 - x)) % 2;
for (int y = 0; y < height; y++)
matrix[(x + y) % width, y] = cellValue;
}
if (!HasPropertyX(matrix))
return matrix;
#if MULTITHREADING
if (_stopSearch)
return null;
#endif
}
return null;
}
#if MULTITHREADING
static long _lyndonWord;
#endif
static IEnumerable<long> GetLyndonWords(int length)
{
long lyndonWord = 0;
long max = (1L << (length - 1)) - 1;
while (lyndonWord <= max)
{
if ((lyndonWord % 2 != 0) && PrecedesReversal(lyndonWord, length))
yield return lyndonWord;
#if MULTITHREADING
lock (_lyndonWordLock)
{
if (_lyndonWord <= max)
_lyndonWord = NextLyndonWord(_lyndonWord, length);
else
yield break;
lyndonWord = _lyndonWord;
}
#else
lyndonWord = NextLyndonWord(lyndonWord, length);
#endif
}
}
static readonly int[] _lookup =
{
32, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4, 7, 17,
0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5, 20, 8, 19, 18
};
static int NumberOfTrailingZeros(uint i)
{
return _lookup[(i & -i) % 37];
}
static long NextLyndonWord(long w, int length)
{
if (w == 0)
return 1;
int currentLength = length - NumberOfTrailingZeros((uint)w);
while (currentLength < length)
{
w += w >> currentLength;
currentLength *= 2;
}
w++;
return w;
}
private static bool PrecedesReversal(long lyndonWord, int length)
{
int shift = length - 1;
long reverse = 0;
for (int i = 0; i < length; i++)
{
long bit = (lyndonWord >> i) % 2;
reverse |= bit << (shift - i);
}
for (int i = 0; i < length; i++)
{
if (reverse < lyndonWord)
return false;
long bit = reverse % 2;
reverse /= 2;
reverse += bit << shift;
}
return true;
}
#if MULTITHREADING
[ThreadStatic]
#endif
static LongSet _left = new LongSet();
#if MULTITHREADING
[ThreadStatic]
#endif
static LongSet _right = new LongSet();
static bool HasPropertyX(int[,] matrix)
{
long[] matrixColumns = GetMatrixColumns(matrix);
if (matrixColumns.Length == 1)
return false;
return HasPropertyXFast(matrixColumns) || MeetInTheMiddle(matrixColumns);
}
static bool MeetInTheMiddle(long[] matrixColumns)
{
long[] leftColumns = matrixColumns.Take(matrixColumns.Length / 2).ToArray();
long[] rightColumns = matrixColumns.Skip(matrixColumns.Length / 2).ToArray();
if (PrepareHashSet(leftColumns, _left) || PrepareHashSet(rightColumns, _right))
return true;
foreach (long columnSum in _left.GetValues())
if (_right.Contains(columnSum))
return true;
return false;
}
static bool PrepareHashSet(long[] columns, LongSet sums)
{
int setSize = (int)System.Numerics.BigInteger.Pow(3, columns.Length);
sums.Reset(setSize, setSize);
foreach (long column in columns)
{
foreach (long sum in sums.GetValues())
if (!sums.Add(sum + column) || !sums.Add(sum - column))
return true;
if (!sums.Add(column) || !sums.Add(-column))
return true;
}
return false;
}
#if MULTITHREADING
[ThreadStatic]
#endif
static LongSet _columnSums = new LongSet();
static bool HasPropertyXFast(long[] matrixColumns)
{
int width = matrixColumns.Length;
int maxColumnCount = width / 3;
_columnSums.Reset(width, SumOfBinomialCoefficients(width, maxColumnCount));
int resetBit, setBit;
for (int k = 1; k <= maxColumnCount; k++)
{
uint columnMask = (1u << k) - 1;
long sum = 0;
for (int i = 0; i < k; i++)
sum += matrixColumns[i];
while (true)
{
if (!_columnSums.Add(sum))
return true;
if (!NextColumnMask(columnMask, k, width, out resetBit, out setBit))
break;
columnMask ^= (1u << resetBit) ^ (1u << setBit);
sum = sum - matrixColumns[resetBit] + matrixColumns[setBit];
}
}
return false;
}
// stolen from Peter Taylor
static bool NextColumnMask(uint mask, int k, int n, out int resetBit, out int setBit)
{
int gap = NumberOfTrailingZeros(~mask);
int next = 1 + NumberOfTrailingZeros(mask & (mask + 1));
if (((k - gap) & 1) == 0)
{
if (gap == 0)
{
resetBit = next - 1;
setBit = next - 2;
}
else if (gap == 1)
{
resetBit = 0;
setBit = 1;
}
else
{
resetBit = gap - 2;
setBit = gap;
}
}
else
{
if (next == n)
{
resetBit = 0;
setBit = 0;
return false;
}
if ((mask & (1 << next)) == 0)
{
if (gap == 0)
{
resetBit = next - 1;
setBit = next;
}
else
{
resetBit = gap - 1;
setBit = next;
}
}
else
{
resetBit = next;
setBit = gap;
}
}
return true;
}
static long[] GetMatrixColumns(int[,] matrix)
{
int width = matrix.GetLength(0);
int height = matrix.GetLength(1);
long[] result = new long[width];
for (int x = 0; x < width; x++)
{
long column = 0;
for (int y = 0; y < height; y++)
{
column *= 13;
if (matrix[x, y] == 1)
column++;
}
result[x] = column;
}
return result;
}
static int SumOfBinomialCoefficients(int n, int k)
{
int result = 0;
for (int i = 0; i <= k; i++)
result += BinomialCoefficient(n, i);
return result;
}
static int BinomialCoefficient(int n, int k)
{
long result = 1;
for (int i = n - k + 1; i <= n; i++)
result *= i;
for (int i = 2; i <= k; i++)
result /= i;
return (int)result;
}
static void PrintMatrix(int[,] matrix)
{
int width = matrix.GetLength(0);
int height = matrix.GetLength(1);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
Console.Write(matrix[x, y]);
Console.WriteLine();
}
Console.WriteLine("Score: {0}/{1} = {2}", width, height, (double)width / height);
}
}
class LongSet
{
private static readonly int[] primes =
{
17, 37, 67, 89, 113, 149, 191, 239, 307, 389, 487, 613, 769, 967, 1213, 1523, 1907,
2389, 2999, 3761, 4703, 5879, 7349, 9187, 11489, 14369, 17971, 22469, 28087, 35111,
43889, 54869, 68597, 85751, 107197, 133999, 167521, 209431, 261791, 327247, 409063,
511333, 639167, 798961, 998717, 1248407, 1560511, 1950643, 2438309, 3047909,
809891, 4762367, 5952959, 7441219, 9301529, 11626913, 14533661, 18167089, 22708867,
28386089, 35482627, 44353297, 55441637, 69302071, 86627603, 108284507, 135355669,
169194593, 211493263, 264366593, 330458263, 413072843, 516341057, 645426329,
806782913, 1008478649, 1260598321
};
private int[] _buckets;
private int[] _nextItemIndexes;
private long[] _items;
private int _count;
private int _minCapacity;
private int _maxCapacity;
private int _currentCapacity;
public LongSet()
{
Initialize(0, 0);
}
private int GetPrime(int capacity)
{
foreach (int prime in primes)
if (prime >= capacity)
return prime;
return int.MaxValue;
}
public void Reset(int minCapacity, int maxCapacity)
{
if (maxCapacity > _maxCapacity)
Initialize(minCapacity, maxCapacity);
else
ClearBuckets();
}
private void Initialize(int minCapacity, int maxCapacity)
{
_minCapacity = GetPrime(minCapacity);
_maxCapacity = GetPrime(maxCapacity);
_currentCapacity = _minCapacity;
_buckets = new int[_maxCapacity];
_nextItemIndexes = new int[_maxCapacity];
_items = new long[_maxCapacity];
_count = 0;
}
private void ClearBuckets()
{
Array.Clear(_buckets, 0, _currentCapacity);
_count = 0;
_currentCapacity = _minCapacity;
}
public bool Add(long value)
{
int bucket = (int)((ulong)value % (ulong)_currentCapacity);
for (int i = _buckets[bucket] - 1; i >= 0; i = _nextItemIndexes[i])
if (_items[i] == value)
return false;
if (_count == _currentCapacity)
{
Grow();
bucket = (int)((ulong)value % (ulong)_currentCapacity);
}
int index = _count;
_items[index] = value;
_nextItemIndexes[index] = _buckets[bucket] - 1;
_buckets[bucket] = index + 1;
_count++;
return true;
}
private void Grow()
{
Array.Clear(_buckets, 0, _currentCapacity);
const int growthFactor = 8;
int newCapacity = GetPrime(_currentCapacity * growthFactor);
if (newCapacity > _maxCapacity)
newCapacity = _maxCapacity;
_currentCapacity = newCapacity;
for (int i = 0; i < _count; i++)
{
int bucket = (int)((ulong)_items[i] % (ulong)newCapacity);
_nextItemIndexes[i] = _buckets[bucket] - 1;
_buckets[bucket] = i + 1;
}
}
public bool Contains(long value)
{
int bucket = (int)((ulong)value % (ulong)_buckets.Length);
for (int i = _buckets[bucket] - 1; i >= 0; i = _nextItemIndexes[i])
if (_items[i] == value)
return true;
return false;
}
public IReadOnlyList<long> GetValues()
{
return new ArraySegment<long>(_items, 0, _count);
}
}
Yapılandırma dosyası:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<gcAllowVeryLargeObjects enabled="true" />
</runtime>
</configuration>