C ++ (sezgisel): 2, 4, 10, 16, 31, 47, 75, 111, 164, 232, 328, 445, 606, 814, 1086
Bu 1 ila 3 alt olmanın Peter Taylor'ın sonucu biraz geriden gelen n=7
, 9
ve 10
. Avantajı çok daha hızlı olmasıdır, bu yüzden daha yüksek değerler için çalıştırabilirim n
. Ve herhangi bir süslü matematik olmadan anlaşılabilir. ;)
Geçerli kod, çalışacak şekilde boyutlandırılmıştır n=15
. Çalışma süreleri, her bir artış için kabaca 4 kat artar n
. Örneğin, 0.008 saniye n=7
, 0.07 saniye n=9
, 1.34 saniye n=11
, 27 saniye n=13
ve 9 dakika idi n=15
.
Kullandığım iki önemli gözlem var:
- Sezgisel değerler üzerinde çalışmak yerine dizileri saymaya çalışır. Bunu yapmak için, önce tüm benzersiz sayma dizilerinin bir listesi oluşturulur.
- Küçük değerlere sahip sayma dizilerinin kullanılması, daha az çözelti alanı ortadan kaldırdığından daha faydalıdır. Bu, diğer değerlerden ila
c
aralığına hariç her sayıya dayanır . Daha küçük değerleri için bu aralık daha küçüktür, yani daha az değer kullanılarak kullanılır.c / 2
2 * c
c
Benzersiz Sayım Dizileri Oluşturma
Tüm değerleri yineleyerek, her biri için sayım dizisini oluşturarak ve ortaya çıkan listeyi benzersizleştirerek bu kaba kuvvet uyguladım. Bu kesinlikle daha verimli bir şekilde yapılabilir, ancak birlikte çalıştığımız değer türleri için yeterince iyidir.
Bu, küçük değerler için son derece hızlıdır. Daha büyük değerler için, ek yük önemli hale gelir. Örneğin n=15
, tüm çalışma zamanının yaklaşık% 75'ini kullanır. Bu kesinlikle çok daha yüksek gitmeye çalışırken bakmak için bir alan olacaktır n=15
. Sadece tüm değerler için sayım dizilerinin bir listesini oluşturmak için kullanılan bellek kullanımı bile sorunlu hale gelmeye başlayacaktır.
Benzersiz sayma dizisi sayısı, değer sayısının yaklaşık% 6'sıdır n=15
. Bu göreceli sayım büyüdükçe küçülür n
.
Sayma Dizisi Değerlerinin Seçimi
Algoritmanın ana kısmı, basit bir açgözlü yaklaşım kullanarak oluşturulan listeden dizi değerlerini saymayı seçer.
Küçük sayımlı sayım dizilerinin kullanılmasının yararlı olduğu teorisine dayanarak, sayım dizileri sayılarının toplamına göre sıralanır.
Daha sonra sırayla kontrol edilirler ve daha önce kullanılan tüm değerlerle uyumluysa bir değer seçilir. Bu nedenle, her adayın daha önce seçilen değerlerle karşılaştırıldığı benzersiz sayma dizilerinden tek bir doğrusal geçiş içerir.
Buluşsal yöntemlerin potansiyel olarak nasıl geliştirilebileceği hakkında bazı fikirlerim var. Ancak bu makul bir başlangıç noktası gibi görünüyordu ve sonuçlar oldukça iyi görünüyordu.
kod
Bu yüksek düzeyde optimize edilmemiştir. Bir noktada daha ayrıntılı bir veri yapım vardı, ancak onu ötesinde genelleştirmek için daha fazla çalışmaya ihtiyaç duyacaktı n=8
ve performans farkı çok önemli görünmüyordu.
#include <cstdint>
#include <cstdlib>
#include <vector>
#include <algorithm>
#include <sstream>
#include <iostream>
typedef uint32_t Value;
class Counter {
public:
static void setN(int n);
Counter();
Counter(Value val);
bool operator==(const Counter& rhs) const;
bool operator<(const Counter& rhs) const;
bool collides(const Counter& other) const;
private:
static const int FIELD_BITS = 4;
static const uint64_t FIELD_MASK = 0x0f;
static int m_n;
static Value m_valMask;
uint64_t fieldSum() const;
uint64_t m_fields;
};
void Counter::setN(int n) {
m_n = n;
m_valMask = (static_cast<Value>(1) << n) - 1;
}
Counter::Counter()
: m_fields(0) {
}
Counter::Counter(Value val) {
m_fields = 0;
for (int k = 0; k < m_n; ++k) {
m_fields <<= FIELD_BITS;
m_fields |= __builtin_popcount(val & m_valMask);
val >>= 1;
}
}
bool Counter::operator==(const Counter& rhs) const {
return m_fields == rhs.m_fields;
}
bool Counter::operator<(const Counter& rhs) const {
uint64_t lhsSum = fieldSum();
uint64_t rhsSum = rhs.fieldSum();
if (lhsSum < rhsSum) {
return true;
}
if (lhsSum > rhsSum) {
return false;
}
return m_fields < rhs.m_fields;
}
bool Counter::collides(const Counter& other) const {
uint64_t fields1 = m_fields;
uint64_t fields2 = other.m_fields;
for (int k = 0; k < m_n; ++k) {
uint64_t c1 = fields1 & FIELD_MASK;
uint64_t c2 = fields2 & FIELD_MASK;
if (c1 > 2 * c2 || c2 > 2 * c1) {
return false;
}
fields1 >>= FIELD_BITS;
fields2 >>= FIELD_BITS;
}
return true;
}
int Counter::m_n = 0;
Value Counter::m_valMask = 0;
uint64_t Counter::fieldSum() const {
uint64_t fields = m_fields;
uint64_t sum = 0;
for (int k = 0; k < m_n; ++k) {
sum += fields & FIELD_MASK;
fields >>= FIELD_BITS;
}
return sum;
}
typedef std::vector<Counter> Counters;
int main(int argc, char* argv[]) {
int n = 0;
std::istringstream strm(argv[1]);
strm >> n;
Counter::setN(n);
int nBit = 2 * n - 1;
Value maxVal = static_cast<Value>(1) << nBit;
Counters allCounters;
for (Value val = 0; val < maxVal; ++val) {
Counter counter(val);
allCounters.push_back(counter);
}
std::sort(allCounters.begin(), allCounters.end());
Counters::iterator uniqEnd =
std::unique(allCounters.begin(), allCounters.end());
allCounters.resize(std::distance(allCounters.begin(), uniqEnd));
Counters solCounters;
int nSol = 0;
for (Value idx = 0; idx < allCounters.size(); ++idx) {
const Counter& counter = allCounters[idx];
bool valid = true;
for (int iSol = 0; iSol < nSol; ++iSol) {
if (solCounters[iSol].collides(counter)) {
valid = false;
break;
}
}
if (valid) {
solCounters.push_back(counter);
++nSol;
}
}
std::cout << "result: " << nSol << std::endl;
return 0;
}
L1[i]/2 <= L2[i] <= 2*L1[i]
.