C ++ (การวิเคราะห์พฤติกรรม): 2, 4, 10, 16, 31, 47, 75, 111, 164, 232, 328, 445, 606, 814, 1086
นี่คือสิ่งที่อยู่เบื้องหลังผลลัพธ์ของ Peter Taylor เล็กน้อยสำหรับn=7
1 9
และ10
3 n
ข้อดีก็คือว่ามันได้เร็วขึ้นมากดังนั้นฉันสามารถเรียกใช้ค่าที่สูงขึ้นของ และสามารถเข้าใจได้โดยไม่ต้องมีคณิตศาสตร์แฟนซี ;)
n=15
รหัสปัจจุบันเป็นมิติที่จะวิ่งขึ้นไป เวลารันเพิ่มขึ้นประมาณ 4 เท่าสำหรับการเพิ่มแต่ละn
ครั้ง ตัวอย่างเช่นมันคือ 0.008 วินาทีสำหรับn=7
0.07 วินาทีสำหรับn=9
, 1.34 วินาทีn=11
, 27 วินาทีn=13
และ 9 นาทีn=15
นาที
มีข้อสังเกตสำคัญสองข้อที่ฉันใช้:
- แทนที่จะจัดการกับค่าด้วยตนเองฮิวริสติกทำงานกับการนับอาร์เรย์ เมื่อต้องการทำเช่นนี้จะมีการสร้างรายการอาร์เรย์การนับที่ไม่ซ้ำกันทั้งหมดก่อน
- การใช้อาร์เรย์การนับที่มีค่าน้อยจะมีประโยชน์มากกว่าเนื่องจากจะช่วยลดพื้นที่การแก้ปัญหาให้น้อยลง สิ่งนี้จะขึ้นอยู่กับการนับแต่ละครั้ง
c
ไม่รวมช่วงของc / 2
ถึง2 * c
จากค่าอื่น ๆ สำหรับค่าที่น้อยลงของc
ช่วงนี้จะเล็กกว่าซึ่งหมายความว่าจะมีการยกเว้นค่าน้อยลงโดยใช้มัน
สร้างอาร์เรย์การนับที่ไม่ซ้ำกัน
ฉันดุร้ายกับสิ่งนี้ซ้ำผ่านค่าทั้งหมดสร้างอาร์เรย์จำนวนสำหรับแต่ละรายการและสร้างรายการผลลัพธ์ที่ไม่ซ้ำกัน สิ่งนี้สามารถทำได้อย่างมีประสิทธิภาพมากขึ้น แต่ก็ดีพอสำหรับค่านิยมที่เราใช้งาน
นี่เป็นสิ่งที่รวดเร็วอย่างยิ่งสำหรับค่าขนาดเล็ก สำหรับค่าที่มากขึ้นค่าโสหุ้ยจะกลายเป็นรูปธรรม ตัวอย่างเช่นสำหรับn=15
ใช้ประมาณ 75% ของรันไทม์ทั้งหมด นี่จะเป็นพื้นที่ที่ควรพิจารณาเมื่อพยายามจะสูงกว่าn=15
มาก แม้เพียงแค่การใช้หน่วยความจำสำหรับสร้างรายการของอาร์เรย์การนับสำหรับค่าทั้งหมดจะเริ่มมีปัญหา
จำนวนของอาร์เรย์การนับที่ไม่ซ้ำกันคือประมาณ 6% ของจำนวนค่า n=15
ของจำนวนค่าสำหรับ จำนวนสัมพัทธ์นี้จะเล็กลงเมื่อn
ใหญ่ขึ้น
การเลือกโลภของการนับค่าอาร์เรย์
ส่วนหลักของอัลกอริทึมเลือกการนับค่าอาร์เรย์จากรายการที่สร้างขึ้นโดยใช้วิธีการโลภที่เรียบง่าย
ตามทฤษฎีที่ใช้การนับอาร์เรย์ที่มีจำนวนน้อยจะเป็นประโยชน์อาร์เรย์การนับจะเรียงลำดับตามผลรวมของการนับของพวกเขา
พวกเขาจะถูกตรวจสอบตามลำดับและค่าจะถูกเลือกหากเข้ากันได้กับค่าที่ใช้ก่อนหน้านี้ทั้งหมด ดังนั้นสิ่งนี้เกี่ยวข้องกับการส่งผ่านแบบเชิงเส้นเดียวผ่านอาร์เรย์การนับที่ไม่ซ้ำกันซึ่งผู้สมัครแต่ละคนจะถูกเปรียบเทียบกับค่าที่เลือกไว้ก่อนหน้านี้
ฉันมีความคิดบางอย่างเกี่ยวกับวิธีปรับปรุงแก้ไขปัญหาที่อาจเกิดขึ้น แต่นี่เป็นจุดเริ่มต้นที่สมเหตุสมผลและผลลัพธ์ดูดีมาก
รหัส
สิ่งนี้ไม่ได้รับการปรับให้เหมาะสมอย่างมาก ฉันมีโครงสร้างข้อมูลที่ซับซ้อนมากขึ้นในบางจุด แต่มันจะต้องมีการทำงานมากขึ้นในการพูดเกินn=8
จริงและความแตกต่างของประสิทธิภาพดูเหมือนจะไม่สำคัญนัก
#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]
เป็น