In C ++ wird der gesamte Speicher eines Computers in Ihrer Nähe aufgelöst
Erzeugt die kürzeste Zeichenfolge, bei der die Berechnung nirgendwo einen Überlauf einer vorzeichenbehafteten 32-Bit-Ganzzahl verursacht (alle Zwischenergebnisse liegen also im Bereich) [-2147483648, 2147483647]
Auf meinem System generiert dies eine Lösung für alle Nummern bis einschließlich 483432
30 Sekunden bei Verwendung von 1,8 G Speicher. Noch höhere Zahlen werden die Speichernutzung schnell explodieren. Die höchste Zahl, die ich auf meinem System verarbeiten kann, ist 5113906
. Die Berechnung dauert fast 9 Minuten und 24 GB. Wenn es fertig ist, hat es intern eine Lösung für398499338
Werte, etwa 9% aller 32-Bit-Ganzzahlen (positiv und negativ)
Benötigt einen C ++ 11 Compiler. Unter Linux kompilieren mit:
g++ -Wall -O3 -march=native -std=gnu++11 -s befour.cpp -o befour
Fügen Sie diese -DINT64
Option hinzu, um einen 64-Bit-Integer-Bereich anstelle von 32-Bit für Zwischenergebnisse zu verwenden (dies beansprucht etwa 50% mehr Zeit und Speicher). Dies erfordert einen eingebauten 128-Bit-Typ. Möglicherweise müssen Sie den gcc-Typ ändern __int128
. Kein Ergebnis, zumindest der Bereich [1..483432]
ändert sich, indem größere Zwischenergebnisse zugelassen werden.
Fügen Sie -DOVERFLOW
als Option hinzu, dass kein größerer ganzzahliger Typ zum Überprüfen auf Überlauf verwendet wird. Dies hat den Effekt, dass ein Überlauf und ein Werteverlauf zugelassen werden.
Wenn Ihr System über tcmalloc ( https://github.com/gperftools/gperftools ) verfügt, können Sie eine Verknüpfung mit diesem Programm herstellen, was zu einem Programm führt, das im Allgemeinen etwas schneller ist und etwas weniger Speicher benötigt. Auf einigen UNIX-Systemen können Sie eine Vorabladung verwenden, z
LD_PRELOAD=/usr/lib/libtcmalloc_minimal.so.4 befour 5
Grundsätzliche Verwendung: Generiere und drucke alle Zahlen bis zum Ziel:
befour target
Optionen:
-a
Drucken Sie auch alle Zahlen aus, die beim Ausarbeiten des Ziels generiert wurden
-c
Drucken Sie auch alle Zahlen aus, die mit einem "Übertrag" (dup) beginnen.
-f
Suchen und drucken Sie die erste Zahl hinter dem Ziel, die nicht generiert wurde
-s
Stoppen, wenn das Ziel generiert wird, auch wenn nicht alle Nummern zuvor generiert wurden
-S
Wie -s
und -f
in einer automatischen Schleife. Sobald das Ziel generiert wurde, suchen Sie die erste noch nicht generierte Nummer und machen Sie diese zum neuen Ziel
-E
Verlasse das Spiel nicht sofort, wenn das Ziel erreicht ist. Beende zuerst alle Saiten der aktuellen Länge
-O
Geben Sie die Zeichenfolgen nicht für alle Zahlen bis zum Ziel aus. Nur die Zeichenfolge für das Ziel
-o
Zulässige Anweisungen (Standardeinstellung ist +-*/:
-b num
Niedrigstes Literal, das verschoben werden kann (Standardeinstellung 0
)
-B num
Höchstes Literal, das verschoben werden kann (Standardeinstellung 9
)
-r num
Das niedrigste erlaubte Zwischenergebnis. Wird verwendet, um Unterlauf zu vermeiden. (Standardeinstellung ist INT32_MIN
,-2147483648
-R num
Das höchstzulässige Zwischenergebnis. Wird verwendet, um ein Überlaufen zu vermeiden. (Standardeinstellung ist INT32_MAX
,2147483647
-m memory
(nur Linux) Beenden, wenn ungefähr so viel zusätzlicher Speicher zugewiesen wurde
Einige interessante Optionskombinationen:
Generieren Sie alle Zahlen bis zum Ziel und berechnen Sie die kleinste Zahl, die einen längeren Generator benötigt als alle diese Zahlen:
befour -fE target
Nur Ziel generieren (-s), nur Ziel drucken (-O)
befour -sO target
Ermitteln Sie die höchste Zahl, die in Ihrem System aufgrund von Zeit- und / oder Speicherbeschränkungen generiert werden kann. (Dies führt dazu, dass Ihr System nicht genügend Speicher hat, wenn Sie es laufen lassen. Subtrahieren Sie 1 von der letzten Ausgabe, nach der Sie gesucht haben und die Sie als letzten sicheren Wert sehen ):
befour -S 1
Generieren Sie Lösungen, ohne negative Zwischenergebnisse zu verwenden ( 30932
ist der erste Wert, der negative Zwischenergebnisse für die kürzeste Zeichenfolge benötigt):
befour -r0 target
Generieren Sie Lösungen, ohne jemals Druck auszuüben 0
(dies scheint nicht zu suboptimalen Lösungen zu führen):
befour -b1 target
Lösungen generieren, einschließlich a..f (10..15)
:
befour -B15 target
Lösungen ohne dup generieren :
(addieren, -r0
da negative Zwischenwerte für diesen Fall nie interessant sind)
befour -r0 -o "+-*/" target
Finden Sie den ersten Wert, der nicht für eine bestimmte Zeichenfolge Länge erzeugt werden kann unter Verwendung von nur +
, -
, *
und /
:
befour -ES -r0 -o "+-*/" 1
Dies wird in der Tat die ersten Begriffe von https://oeis.org/A181898 generieren , wird jedoch abweichend sein, 14771
da wir eine abschneidende Division verwenden, so dass die Zahl mit einer Zeichenfolge der Länge 13 anstelle der Länge 15 als OEIS-Reihe erfolgen kann erwartet:
14771: 13: 99*9*9*4+9*4/
Anstatt von
14771: 15: 19+5*6*7*9+7*8+
Da eine Division ohne Kürzung sinnlos erscheint, kann die OEIS-Reihe besser mit erzeugt werden
befour -ES -r0 -o"+-*" 1
Unter der Annahme, dass die Aufteilung unbrauchbar bleibt, gab mir dies 3 zusätzliche Begriffe, bevor ich aus dem Gedächtnis herauskam:
10, 19, 92, 417, 851, 4237, 14771, 73237, 298609, 1346341, 6176426, 25622578
Eine andere Version dieses Programms, die einen Teil der Daten in externen Dateien speichert, fügt 135153107 und 675854293 hinzu, nach denen alle 32-Bit-Ganzzahlen generiert wurden.
befour.cpp
/*
Compile using something like:
g++ -Wall -O3 -march=native -std=gnu++11 -s befour.cpp -o befour
*/
#include <iostream>
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
#include <limits>
#include <climits>
#include <cstdint>
#include <cstdlib>
#include <chrono>
#include <unordered_map>
using namespace std;
#ifdef __GNUC__
# define HOT __attribute__((__hot__))
# define COLD __attribute__((__cold__))
# define NOINLINE __attribute__((__noinline__))
# define LIKELY(x) __builtin_expect(!!(x),1)
# define UNLIKELY(x) __builtin_expect(!!(x),0)
#else // __GNUC__
# define HOT
# define COLD
# define NOINLINE
# define LIKELY(x) (x)
# define UNLIKELY(x) (x)
#endif // __GNUC__
#ifdef INT64
using Int = int64_t; // Supported value type
# ifndef OVERFLOW
using Int2 = __int128; // Do calculations in this type. Check overflow
# endif // OVERFLOW
#else // INT64
using Int = int32_t; // Supported value type
# ifndef OVERFLOW
using Int2 = int64_t; // Do calculations in this type. Check overflow
# endif // OVERFLOW
#endif // INT64
#ifdef OVERFLOW
using Int2 = Int;
#endif // OVERFLOW
// Supported value range
Int2 MIN = numeric_limits<Int>::lowest();
Int2 MAX = numeric_limits<Int>::max();
Int HALF_MIN, HALF_MAX;
// The initial values we can push
Int ATOM_MIN = 0;
Int ATOM_MAX = 9;
bool all = false; // Output all reached values
bool all_carry = false; // Output all values reachable using carry
bool early_exit = true; // Exit before finishing level if goal reached
bool find_hole = false; // Look for first unconstructed > target
bool output = true; // Output [1..target] instead of just target
bool single = false; // Only go for target instead of [1..target]
bool explore = false; // Don't stop, increase N until out of memory
bool do_dup = false; // Use operator :
bool do_multiply= false; // Use operator *
bool do_add = false; // Use operator +
bool do_subtract= false; // Use operator -
bool do_divide = false; // Use operator /
char const* operators = "+-*/:"; // Use these operators
size_t max_mem = SIZE_MAX; // Stop if target memory reached
size_t const MEM_CHECK = 1000000;
chrono::steady_clock::time_point start;
NOINLINE size_t get_memory(bool set_base_mem = false) {
static size_t base_mem = 0;
size_t const PAGE_SIZE = 4096;
// Linux specific. Won't hurt on other systems, just gets no result
size_t mem = 0;
std::ifstream statm;
statm.open("/proc/self/statm");
statm >> mem;
mem *= PAGE_SIZE;
if (set_base_mem) base_mem = mem;
else mem -= base_mem;
return mem;
}
// Handle commandline options.
// Simplified getopt for systems that don't have it in their library (Windows..)
class GetOpt {
private:
string const options;
char const* const* argv;
int nextchar = 0;
int optind = 1;
char ch = '?';
char const* optarg = nullptr;
public:
int ind() const { return optind; }
char const* arg() const { return optarg; }
char option() const { return ch; }
GetOpt(string const options_, char const* const* argv_) :
options(options_), argv(argv_) {}
char next() {
while (1) {
if (nextchar == 0) {
if (!argv[optind] ||
argv[optind][0] != '-' ||
argv[optind][1] == 0) return ch = 0;
if (argv[optind][1] == '-' && argv[optind][2] == 0) {
++optind;
return ch = 0;
}
nextchar = 1;
}
ch = argv[optind][nextchar++];
if (ch == 0) {
++optind;
nextchar = 0;
continue;
}
auto pos = options.find(ch);
if (pos == string::npos) ch = '?';
else if (options[pos+1] == ':') {
if (argv[optind][nextchar]) {
optarg = &argv[optind][nextchar];
} else {
optarg = argv[++optind];
if (!optarg) return ch = options[0] == ':' ? ':' : '?';
}
++optind;
nextchar = 0;
}
return ch;
}
}
};
using ms = chrono::milliseconds;
Int missing, N;
size_t cached, cached_next;
uint8_t const CARRY_MASK = '\x80';
uint8_t const LITERAL = 0;
struct How {
// Describes how to construct a number
Int left;
Int right;
uint8_t ops, op;
How(uint8_t ops_, uint8_t op_, Int carry_=0, Int left_=0, Int right_=0) :
left(left_),
right(right_),
ops(ops_),
op(carry_ ? CARRY_MASK | op_ : op_)
{}
How() = default;
How(How&&) = default;
How& operator=(How&&) = default;
static How const* predict(Int carry, Int value, int& ops);
static void print_predicted(ostream& out, Int carry, Int value, How const* Value = nullptr);
void print(ostream& out, Int carry = 0, bool length = false) const;
};
ostream& operator<<(ostream& out, How const& how) {
how.print(out, 0, true);
return out;
}
using NumSet = vector<Int>;
using NumSets = vector<NumSet>;
struct Known: public unordered_map<Int, How>
{
void store(NumSet& L, Int accu, uint8_t ops, uint8_t op,
Int left=0, Int carry_right=0, Int right=0) {
++cached;
emplace(accu, How(ops, op, carry_right, left, right));
// operator[](accu) = How(ops, op, carry_right, left, right);
L.emplace_back(accu);
}
void maybe_store(Known const& known0, NumSet& L,
Int accu, uint8_t ops, uint8_t op,
Int carry_left, Int left, Int carry_right, Int right) {
if (count(accu)) return;
if (carry_left) {
auto found = known0.find(accu);
// If we can do as good or better without carry use that
if (found != known0.end() && found->second.ops <= ops) return;
}
store(L, accu, ops, op, left, carry_right, right);
if (carry_left) return;
if (single) {
if (UNLIKELY(accu == N)) known0.maybe_explore();
} else if (1 <= accu && accu <= N) --missing;
}
NOINLINE void maybe_explore() const COLD {
--missing;
if (explore && early_exit) do_explore();
}
NOINLINE void do_explore() const COLD {
auto i = N;
while (i < MAX && count(++i));
auto end = chrono::steady_clock::now();
auto elapsed = chrono::duration_cast<ms>(end-start).count();
cerr << "Found " << N << " at " << elapsed / 1000. << " s";
auto mem = get_memory();
if (mem) cerr << " (" << mem / 1000 / 1000. << " MB)";
if (i < MAX || !count(i)) {
cerr << ", now looking for " << i << endl;
N = i;
++missing;
} else
cerr << ", every value has now been generated" << endl;
}
};
struct KnowHow {
// Describes all numbers we know how to construct
NumSets num_sets;
Known known;
KnowHow() = default;
~KnowHow() = default;
KnowHow(KnowHow const&) = delete;
KnowHow& operator=(KnowHow const&) = delete;
};
// Describes all numbers we know how to construct for a given carry
// Key 0 is special: the numbers we can construct without carry (the solutions)
unordered_map<Int, KnowHow> known_how;
// Try to predict if a subtree is a delayed How and avoid descending
// into it (since it may not exist yet)
How const* How::predict(Int carry, Int value, int& ops) {
How* Value;
if (carry) {
if (value == carry) {
Value = nullptr;
ops = 0;
} else {
Value = &known_how.at(carry).known.at(value);
ops = Value->ops;
}
} else {
if (ATOM_MIN <= value && value <= ATOM_MAX) {
Value = nullptr;
ops = 0;
} else {
Value = &known_how.at(0).known.at(value);
ops = Value->ops;
}
}
return Value;
}
void How::print_predicted(ostream& out, Int carry, Int value, How const* Value) {
if (Value) Value->print(out, carry);
else if (carry) out << ":";
else if (value > 9) out << static_cast<char>(value-10+'a');
else out << value;
}
void How::print(ostream& out, Int carry_left, bool length) const {
if (length) out << 2*ops+1 << ": ";
Int carry_right = 0;
auto op_ = op;
switch(op_) {
case LITERAL:
How::print_predicted(out, 0, left);
break;
case '*' | CARRY_MASK:
case '/' | CARRY_MASK:
case '+' | CARRY_MASK:
case '-' | CARRY_MASK:
carry_right = left;
op_ &= ~CARRY_MASK;
// Intentional drop through
case '*':
case '/':
case '+':
case '-':
{
int left_ops, right_ops;
auto Left = How::predict(carry_left, left, left_ops);
// Int right = 0;
auto Right = How::predict(carry_right, right, right_ops);
// Sanity check: tree = left_tree + root + right_tree
if (ops != left_ops + right_ops +1) {
char buffer[80];
snprintf(buffer, sizeof(buffer),
"Broken number %d %c %d, length %d != %d + %d + 1",
static_cast<int>(left), op_, static_cast<int>(right),
ops, left_ops, right_ops);
throw(logic_error(buffer));
}
How::print_predicted(out, carry_left, left, Left);
How::print_predicted(out, carry_right, right, Right);
}
// Intentional drop through
case ':':
out << op_;
break;
default:
throw(logic_error("Unknown op " + string{static_cast<char>(op_)}));
break;
}
}
// carryX indicates Xv was reached using carry. If not we also know [L, known] is known_how[0]
// carryY indicates Y was reached using carry (carryY == Xv if so)
void combine(NumSet& L, Known& known, Known const& known0, int ops, Int carryX, Int2 Xv, Int carryY, NumSet const&Y) HOT;
void combine(NumSet& L, Known& known, Known const& known0, int ops, Int carryX, Int2 Xv, Int carryY, NumSet const&Y) {
for (Int Yv: Y) {
// Yv == 0 can never lead to an optimal calculation
if (Yv == 0) continue;
Int2 accu;
if (do_multiply) {
accu = Xv * Yv;
if (accu <= MAX && accu >= MIN)
known.maybe_store(known0, L, accu, ops, '*', carryX, Xv, carryY, Yv);
}
if (do_add) {
accu = Xv + Yv;
if (accu <= MAX && accu >= MIN)
known.maybe_store(known0, L, accu, ops, '+', carryX, Xv, carryY, Yv);
}
if (do_subtract) {
accu = Xv - Yv;
if (accu <= MAX && accu >= MIN)
known.maybe_store(known0, L, accu, ops, '-', carryX, Xv, carryY, Yv);
}
if (do_divide) {
accu = Xv / Yv;
if (accu <= MAX && accu >= MIN)
known.maybe_store(known0, L, accu, ops, '/', carryX, Xv, carryY, Yv);
}
}
}
// value was constructed using a carry if and only if value != 0
NumSet const& level(KnowHow& known_how0, Int value, int ops) HOT;
NumSet const& level(KnowHow& known_how0, Int value, int ops) {
auto& from_value = known_how[value];
if (from_value.num_sets.size() <= static_cast<size_t>(ops)) {
auto& known = from_value.known;
if (from_value.num_sets.size() != static_cast<size_t>(ops)) {
if (value == 0 || ops != 1)
throw(logic_error("Unexpected level skip"));
// This was because of delayed carry creation.
// The delay is over. Create the base case
from_value.num_sets.resize(ops+1);
known.store(from_value.num_sets[0], value, 0, ':', value);
} else
from_value.num_sets.resize(ops+1);
auto& L = from_value.num_sets[ops];
if (ops == 0) {
if (value) {
known.store(L, value, ops, ':', value);
} else {
for (auto i = ATOM_MIN; i <= ATOM_MAX; ++i) {
if (single) {
if (i == N) --missing;
} else {
if (0 < i && i <= N) --missing;
}
known.store(L, i, 0, LITERAL, i);
}
}
} else {
auto& known0 = known_how0.known;
// for (auto k=ops-1; k>=0; --k) {
for (auto k=0; k<ops; ++k) {
auto const& X = from_value.num_sets[ops-1-k];
auto const& Y = known_how0.num_sets[k];
for (Int Xv: X) {
// Plain combine must come before carry combine so a plain
// solution will prune a same length carry solution
combine(L, known, known0, ops, value, Xv, 0, Y);
if (!missing && early_exit) goto DONE;
if (do_dup && (Xv > ATOM_MAX || Xv < ATOM_MIN)) {
// Dup Xv, construct something using k operators, combine
if (k == 0 && Xv != 0) {
// Delay creation of carry known_how[Xv] for 1 level
// This is purely a memory and speed optimization
// Subtraction gives 0 which is never optimal
// Division gives 1 which is never optimal
// Multiplication gives Xv ** 2
// Could be == Xv if Xv== 0 or Xv == 1, but will be
// pruned by atom - atom or atom / atom
Int2 accu = Xv;
accu *= accu;
if (accu <= MAX && accu >= MIN) {
known.maybe_store(known0, L, accu, ops, '*',
value, Xv, Xv, Xv);
}
// Addition gives Xv * 2 (!= Xv)
if (HALF_MIN <= Xv && Xv <= HALF_MAX)
known.maybe_store(known0, L, 2*Xv, ops, '+',
value, Xv, Xv, Xv);
} else {
auto& Z = level(known_how0, Xv, k);
combine(L, known, known0, ops, value, Xv, Xv, Z);
}
if (!missing && early_exit) goto DONE;
}
if (max_mem != SIZE_MAX && cached > cached_next) {
cached_next = cached + MEM_CHECK;
if (get_memory() >= max_mem) goto DONE;
}
}
}
}
// L.shrink_to_fit();
}
DONE:
return from_value.num_sets[ops];
}
void my_main(int argc, char const* const* argv) {
GetOpt options("acfm:sSEOo:b:B:r:R:", argv);
while (options.next())
switch (options.option()) {
case 'a': all = true; break;
case 'b': {
auto tmp = atoll(options.arg());
ATOM_MIN = static_cast<Int>(tmp);
if (static_cast<long long int>(ATOM_MIN) != tmp)
throw(range_error("ATOM_MIN is out of range"));
break;
}
case 'B': {
auto tmp = atoll(options.arg());
ATOM_MAX = static_cast<Int>(tmp);
if (static_cast<long long int>(ATOM_MAX) != tmp)
throw(range_error("ATOM_MAX is out of range"));
break;
}
case 'c': all_carry = true; break;
case 'f': find_hole = true; break;
case 'm': max_mem = atoll(options.arg()); break;
case 'S': explore = true; // intended drop through to single
case 's': single = true; break;
case 'o': operators = options.arg(); break;
case 'E': early_exit = false; break;
case 'r': {
auto tmp = atoll(options.arg());
MIN = static_cast<Int>(tmp);
if (static_cast<long long int>(MIN) != tmp)
throw(range_error("MIN is out of range"));
break;
}
case 'R': {
auto tmp = atoll(options.arg());
MAX = static_cast<Int>(tmp);
if (static_cast<long long int>(MAX) != tmp)
throw(range_error("MAX is out of range"));
break;
}
case 'O': output = false; break;
default:
cerr << "usage: " << argv[0] << " [-a] [-c] [-f] [-D] [-E] [-O] [-s] [-b atom_min] [-B atom_max] [r range_min] [-R range_max] [-m max_mem] [max]" << endl;
exit(EXIT_FAILURE);
}
// Avoid silly option combinations
if (MIN > MAX) throw(logic_error("MIN above MAX"));
if (ATOM_MIN > ATOM_MAX) throw(logic_error("ATOM_MIN above ATOM_MAX"));
if (ATOM_MIN < 0) throw(range_error("Cannot represent negative atoms"));
if (ATOM_MAX > 35) throw(range_error("Cannot represent atoms > 35"));
if (ATOM_MIN < MIN) throw(range_error("ATOM_MIN is out of range"));
if (ATOM_MAX > MAX) throw(range_error("ATOM_MAX is out of range"));
HALF_MIN = MIN / 2;
HALF_MAX = MAX / 2;
for (auto ops=operators; *ops; ++ops)
switch(*ops) {
case '*': do_multiply = true; break;
case '/': do_divide = true; break;
case '+': do_add = true; break;
case '-': do_subtract = true; break;
case ':': do_dup = true; break;
default:
throw(logic_error("Unknown operator"));
}
long long int const NN =
options.ind() < argc ? atoll(argv[options.ind()]) : 1;
if (NN < MIN || NN > MAX)
throw(range_error("Target number is out of range"));
N = NN;
if (N < 1) {
single = true;
output = false;
}
cerr << "N=" << N << ", using " << sizeof(Int) * CHAR_BIT << " bits without overflow" << endl;
missing = single ? 1 : N;
cached = cached_next = 0;
auto& known_how0 = known_how[0];
auto& known = known_how0.known;
auto mem = get_memory(true);
if (!mem && max_mem != SIZE_MAX)
throw(runtime_error("Cannot get memory usage on this system"));
// Start calculation
start = chrono::steady_clock::now();
// Fill in initial values [0..9]
level(known_how0, 0, 0);
// Grow number of allowed operations until all requested numbers are reached
// for (auto ops=1; ops <=5; ++ops) {
for (auto ops=1;;++ops) {
if (missing == 0) {
if (!explore) break;
known_how0.known.do_explore();
if (missing == 0) break;
}
if (max_mem != SIZE_MAX && get_memory() >= max_mem) break;
auto end = chrono::steady_clock::now();
auto elapsed = chrono::duration_cast<ms>(end-start).count();
cerr << "Reaching for " << 2*ops+1 << " instructions at " << elapsed/1000. << " s";
if (mem) cerr << " (" << get_memory() / 1000 / 1000. << " MB)";
cerr << endl;
auto old_cached = cached;
level(known_how0, 0, ops);
if (cached == old_cached) {
cerr << "Oops, all possible numbers have been generated and we still weren't finished" << endl;
break;
}
}
// We are done generating all numbers.
auto end = chrono::steady_clock::now();
// Report the result
// length = 2*ops + 1
Int limit = known_how0.num_sets.size()*2-1;
cerr << "Some numbers needed " << limit << " instructions" << endl;
auto elapsed = chrono::duration_cast<ms>(end-start).count();
start = end;
stringstream out;
out << "Calculation: " << elapsed/1000. << " s\n";
for (auto i = output ? 1 : N; i <= N; ++i) {
if (single || missing) {
auto got = known.find(i);
if (got != known.end())
cout << i << ": " << got->second << "\n";
else
cout << i << " not generated\n";
} else
cout << i << ": " << known.at(i) << "\n";
}
if (output) {
end = chrono::steady_clock::now();
elapsed = chrono::duration_cast<ms>(end-start).count();
start = end;
out << "Printing: " << elapsed/1000. << " s\n";
}
if (find_hole) {
Int hole;
for (auto i = single ? 1 : N+1; 1; ++i) {
if (!known_how0.known.count(i) || i == 0) {
hole = i;
break;
}
}
out << "First missing value " << hole << "\n";
end = chrono::steady_clock::now();
elapsed = chrono::duration_cast<ms>(end-start).count();
start = end;
out << "Missing: " << elapsed/1000. << " s\n";
}
if (all) {
for (auto const& entry: known_how0.known) {
cout << entry.first << ": " << entry.second << "\n";
}
end = chrono::steady_clock::now();
elapsed = chrono::duration_cast<ms>(end-start).count();
start = end;
out << "All: " << elapsed/1000. << " s\n";
}
if (all_carry) {
for (auto const& carry: known_how) {
auto carry_left = carry.first;
if (carry_left == 0) continue;
cout << "Carry " << carry_left << "\n";
for (auto const& how: carry.second.known) {
cout << " " << how.first << ": ";
how.second.print(cout, carry_left, true);
cout << "\n";
}
}
end = chrono::steady_clock::now();
elapsed = chrono::duration_cast<ms>(end-start).count();
start = end;
out << "All carry: " << elapsed/1000. << " s\n";
}
mem = get_memory();
if (mem) cerr << "used about " << mem / 1000 / 1000. << " MB\n";
cerr << out.str();
cerr << "Cached " << cached << " results = " << known.size() << " plain + " << cached - known.size() << " carry" << endl;
}
int main(int argc, char const* const* argv) {
try {
my_main(argc, argv);
} catch(exception& e) {
cerr << "Error: " << e.what() << endl;
quick_exit(EXIT_FAILURE);
}
// Cleaning up the datastructures can take ages
quick_exit(EXIT_SUCCESS);
}
Einige Testfälle:
1: 1: 1
11: 3: 29+
26: 5: 29*8+
27: 3: 39*
100: 5: 19+:*
2431: 9: 56*9*9*1+
3727: 9: 69*7+:*6+
86387: 11: 67*:*1-7*7*
265729: 11: 39*:*:*2/9+
265620: 13: 99*::*6/*7+3*
1921600: 9: 77*:*:*3/
21523360: 9: 99*:*:*2/
57168721: 11: 99*6+:*8-:*
30932: 11: 159*-:4*:*+
56
direkt in den Stapel hineinschieben können, wie können wir dann78
in den Stapel hineinschieben?