0〜9の数字と4つの操作、さらに1つ余分な数字をすばやく表現する


14

説明

Befungeは、stacksを使用する2次元プログラムです

つまり、5 + 6を行うには、と書く56+、という意味です。

56+
5    push 5 into stack
 6   push 6 into stack
  +  pop the first two items in the stack and add them up, and push the result into stack

(to those of you who do not know stacks, "push" just means add and "pop" just means take off)

ただし、数値56をスタックに直接プッシュすることはできません。

そうするために、我々は書かなければならない78*、代わりにその乗算を7して8、スタックに製品をプッシュします。

詳細

から1までの各番号についてn、これらの文字のみで構成される文字列を見つけます0123456789+-*/:(モジュロを使用しません%)。

目標は、上記の形式を使用して、番号を表すことができる最短の文字列を見つけることです。

たとえば、入力がの場合123、出力はになります67*9:*+。出力は左から右に評価する必要があります。

許容される出力が複数ある場合(たとえば許容される場合もあります99*67*+)、いずれかを出力できます(すべてを出力してもボーナスはありません)。

さらなる説明

それでも67*9:*+評価方法がわからない場合123は、詳細な説明があります。

stack    |operation|explanation
          67*9:*+
[6]       6         push 6 to stack
[6,7]      7        push 7 to stack
[42]        *       pop two from stack and multiply, then put result to stack
[42,9]       9      push 9 to stack
[42,9,9]      :     duplicate the top of stack
[42,81]        *    pop two from stack and multiply, then put result to stack
[123]           +   pop two from stack and add, then put result to stack

TL; DR

プログラムは、上記で指定した形式を使用して、入力(数値)を表すことができる最短の文字列を見つける必要があります。

得点

  • すでに最短のコードでそれを行っています。今回は、サイズは関係ありません。
  • 選択した言語には、オペレーティングシステム(Windows 7 Enterprise)用の無料のコンパイラ/インタープリターが必要です。
  • コンパイラー/インタープリターへのリンクを含めるとボーナスが得られます(面倒です)。
  • 可能であれば、私の便宜のためにタイマーを含めてください。タイマーからの出力は有効です。
  • スコアはn1分で最大になります。
  • つまり、プログラムは必要な表現を1以降から印刷する必要があります。
  • いいえハードんコーディングを除き、09

(詳細)仕様

  • 任意の数値に必要な長さよりも長い文字列を出力する場合、プログラムは無効です。
  • 1/0=ERROR
  • 5/2=2(-5)/2=-2(-5)/(-2)=25/(-2)=-2

明確化

-であるsecond-top minus topことを意味し、92-リターンを7

同様に、/isはsecond-top divide top92/返すことを意味します4

サンプルプログラム

ルア

深さ優先検索を使用します。

local function div(a,b)
    if b == 0 then
        return "error"
    end
    local result = a/b
    if result > 0 then
        return math.floor(result)
    else
        return math.ceil(result)
    end
end

local function eval(expr)
    local stack = {}
    for i=1,#expr do
        local c = expr:sub(i,i)
        if c:match('[0-9]') then
            table.insert(stack, tonumber(c))
        elseif c == ':' then
            local a = table.remove(stack)
            if a then
                table.insert(stack,a)
                table.insert(stack,a)
            else
                return -1
            end
        else
            local a = table.remove(stack)
            local b = table.remove(stack)
            if a and b then
                if c == '+' then
                    table.insert(stack, a+b)
                elseif c == '-' then
                    table.insert(stack, b-a)
                elseif c == '*' then
                    table.insert(stack, a*b)
                elseif c == '/' then
                    local test = div(b,a)
                    if test == "error" then
                        return -1
                    else
                        table.insert(stack, test)
                    end
                end
            else
                return -1
            end
        end
    end
    return table.remove(stack) or -1
end

local samples, temp = {""}, {}

while true do
    temp = {}
    for i=1,#samples do
        local s = samples[i]
        if eval(s) ~= -1 or s == "" then for n in ("9876543210+-*/:"):gmatch(".") do
            table.insert(temp, s..n)
        end end
    end
    for i=1,#temp do
        local test = eval(temp[i])
        if input == test then
            print(temp[i])
            return
        end
    end
    samples = temp
end

待っ56て、スタックに直接プッシュできない場合、どうすればスタックにプッシュでき78ますか?
R.ガプス

5656個を直接スタックにプッシュすることはできませんが、77個と88個を別々にスタックにプッシュすることはできます。
リーキー修道女

1
@ R.Kap:56Befungeのようなことをすると、数字がプッシュされるため、のスタックになり[5, 6]ます。取得するには 56を、あなたがプッシュする必要があり7、その後、8スタックに、そしてその後、乗算それらは、スタック上の番号56を取得します。
エレンディアスターマン

1
:物事がかなり複雑になるので、テストケースの良いリストを提供することをお勧めします。たとえば、86387
Sp3000

1
5秒で最大の整数は悪い指標です。大きな数値の計算時間は単調に増加しないため、多くのソリューションは、一部の数値が近くの数値ではるかに高速または低速であるにもかかわらず、計算が困難な同じ数値のままになることがあります。
スパー

回答:


7

C ++、近くのコンピューター上のすべてのメモリを爆発させる

計算が符号付き32ビット整数のオーバーフローを引き起こさない最短文字列を生成します(したがって、すべての中間結果は範囲内にあります [-2147483648, 2147483647]

私のシステムでは、4834321.8Gメモリを使用しながら、30秒以内のすべての数値のソリューションが生成されます。さらに大きな数値を設定すると、メモリ使用量が急速に爆発します。私のシステムで処理できる最大数は5113906です。計算には約9分24GBかかります。終了する398499338と、32ビット整数(正と負)の約9%に値の解決策が内部的にあります

C ++ 11コンパイラが必要です。Linuxでのコンパイル:

g++ -Wall -O3 -march=native -std=gnu++11 -s befour.cpp -o befour

-DINT64中間結果に32ビットではなく64ビット整数範囲を使用するオプションとして追加します(これにより、約50%の時間とメモリが使用されます)。これには、組み込みの128ビットタイプが必要です。gccタイプを変更する必要がある場合があり__int128ます。[1..483432]より大きな中間結果を許可することにより、少なくとも範囲が変化するという結果はありません。

-DOVERFLOWより大きな整数型を使用してオーバーフローをチェックしないオプションとして追加します。これには、オーバーフローと値のラッピングを許可する効果があります。

システムにtcmalloc(https://github.com/gperftools/gperftools)がある場合、それとリンクして、通常は少し高速で、メモリ使用量がわずかに少ないプログラムを作成できます。一部のUNIXシステムでは、プリロードを使用できます。たとえば

LD_PRELOAD=/usr/lib/libtcmalloc_minimal.so.4 befour 5

基本的な使用法:ターゲットまでのすべての数値を生成して印刷します。

befour target

オプション:

  • -a また、ターゲットのワークアウト中に生成されたすべての数値を印刷します
  • -c また、「キャリー」(dup)で始まるすべての数値を出力します
  • -f 生成されなかったターゲットを超える最初の番号を見つけて印刷します
  • -s 以前にすべての数値が生成されていなくても、ターゲットが生成された場合に停止します
  • -S同様-s-f自動ループインチ ターゲットが生成されるとすぐに、まだ生成されていない最初の番号を見つけて、新しいターゲットを作成します
  • -E目標に到達したときにすぐに終了しないでください。最初に現在の長さのすべての文字列を終了します
  • -Oターゲットまでのすべての数値の文字列を出力しないでください。ターゲットの文字列のみ
  • -o 許可された指示(デフォルトは +-*/:
  • -b numプッシュ可能な最小のリテラル(デフォルトは0
  • -B numプッシュ可能な最高のリテラル(デフォルトは9
  • -r num許容される最低の中間結果。アンダーフローを回避するために使用されます。(デフォルトはINT32_MIN-2147483648
  • -R num許容される最高の中間結果。オーバーフローを回避するために使用されます。(デフォルトはINT32_MAX2147483647
  • -m memory (Linuxのみ)ほぼこのくらいの余分なメモリが割り当てられたときに終了します

いくつかの興味深いオプションの組み合わせ:

ターゲットまでのすべての数値を生成し、これらすべての数値よりも長いジェネレーターを必要とする最小の数値を計算します。

befour -fE target

ターゲットのみを生成(-s)、ターゲットのみを印刷(-O)

befour -sO target

与えられた時間および/またはメモリの制約から、システムで生成可能な最大数を見つけます(これにより、システムを実行したままにしておくと、システムのメモリが不足します。 ):

befour -S 1

負の中間結果を使用せずにソリューションを生成します(30932最短の文字列に対して負の中間結果を必要とする最初の値です):

befour -r0 target

プッシュすることなくソリューションを生成します0(これは最適でないソリューションにつながるとは思われません)。

befour -b1 target

以下を含むソリューションを生成しますa..f (10..15)

befour -B15 target

dupを使用せずにソリューションを生成します:-r0この場合、負の中間値は決して面白くないため追加します)

befour -r0 -o "+-*/" target

ちょうど使用して、指定された文字列の長さのために生成することができない最初の値を検索し+-*/

befour -ES -r0 -o "+-*/" 1

これにより、実際にhttps://oeis.org/A181898の最初のいくつかの用語が生成されます14771、OEISシリーズのように長さ15ではなく長さ13の文字列で番号を実行できるように、切り捨て除算を使用するため、発散し始めます期待するもの:

14771: 13: 99*9*9*4+9*4/

の代わりに

14771: 15: 19+5*6*7*9+7*8+

切り捨て除算がないと無意味に見えるため、OEISシリーズは、

befour -ES -r0 -o"+-*" 1

部門が役に立たないと仮定すると、これは私が記憶を失う前に3つの余分な用語を与えました:

10, 19, 92, 417, 851, 4237, 14771, 73237, 298609, 1346341, 6176426, 25622578

データの一部を外部ファイルに保存するこのプログラムの別のバージョンでは、135153107と675854293が追加され、その後すべての32ビット整数が生成されます。

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);
}

いくつかのテストケース:

  • 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*:*+

問題ありません。問題の難しさを考えると、これは非常に高速です。ちょっとしたトラブル:38950002あなたのプログラムはを与える89*7+:::**1-*ので、これはかなり良いですが299*-::*:*+、もっと短くすることもできます。これは、負の数について私が持っていた疑いを裏付けていると思います
...-Sp3000

@ Sp3000:残念なことに、私は正の数しか考えていませんでした。また、負の数を扱うが、私はそれは厳しいメモリと速度のヒットを取ることを期待するプログラムを拡張することは難しいことではありません
トンHospel

@ Sp3000負のテンポラリ用に更新されました。到達可能範囲は実際にかなり下がった
トンホスペル

int main(int argc, char const* const* argv)私は平均的なジョーよりCの方がよくわかりませんが、これは何ですか?charへのconstポインターへのconstポインター?それはそうあるべきではありませんchar const *argv[]か(またはchar const **argvあなたがその筋金入りの場合)?

@cat(配列の)定数char(の配列)へのポインターです。トップレベルのポインターを定数にするために、argvの直前にも別のconstを追加する必要があります(argvも変更しないので機能します)。基本的に、引数または引数へのポインターを変更しないことを約束します。
トンホスペル16年

2

JavaScriptノードブルートフォース

プログラムファイルbfCodes.js

function bfCodes( n)
{   var odo = [0], valid = true, valCount=1;

    const vDUP = 10, vADD = 11, vSUB = 12, vMUL=13, vDIV = 14, vMAX = vDIV;
    const vCHARS = "0123456789:+-*/";

    function inc(sd) // increment significant digit, lsd = 0
    {   if(sd >= odo.length) { odo.push(0); console.log("length: " + (sd+1)); ++valCount; return;}
        var v = ++odo[sd]; // increment and read the base 15 odometer digit
        if( v == vDUP)
            if( valCount) {++valCount; return}
            else { odo[ sd] = vMAX; --valCount; valid = false; return;}

        if( v == vADD)
        {    if( (--valCount) < 1) { valid = false; odo[ sd] = vMAX; return;};
        }
        if( v > vMAX) { odo[sd] = 0; ++valCount; valid = true; inc(sd+1); return;}
    }

    function bfDecode( odo)
    {   var a,b,stack = [];
        for(var i = odo.length; i--;)
        {   var v = odo[ i];
            if( v < 10) { stack.push( v); continue;};
            switch(v) {
            case vDUP: stack.push( stack[stack.length-1]); continue;
            case vADD: b=stack.pop(); stack.push( stack.pop()+b); continue;
            case vMUL: b=stack.pop(); stack.push(stack.pop()*b); continue;
            case vDIV: b=stack.pop(); if(!b) return undefined; a = stack.pop(); 
                stack.push( (a < 0 ? b < 0 : b > 0) ? (a/b)>>0 : -(-a/b >>0)); continue;
            }
        }
        return stack[0];
    }
    var codes = [], value;
    for( var got = 0; got < n;)
    {   inc(0);
        if(!valid) continue;
        if(!(value = bfDecode( odo))) continue;
        if( value <= 0 || value > n || codes[ value]) continue;
        ++got;
        for(var i = odo.length, s=""; i--;)  s+=vCHARS[ odo[i]];
        codes[ value] = s;
    }
    return codes;
}

function main( args) // node, script, number
{   n = parseInt( args[2]);
    if(isNaN(n)){ console.log("\nTry:  node bfCodes number\nfor script saved as bfCodes.js"); return;}
    console.log("\ngenerating befunge code for numbers up to " + n);
    var start = Date.now();
    var codes = bfCodes(n);
    var end = Date.now();
    console.log("befunge codes:");
    for( var i = 1; i <=n; ++i) console.log( i + ": " + codes[i]);
    console.log(end-start + " msec");
}
main( process.argv);

Windowsで実行する

  1. ダウンロードとインストール Nodejs、のchrome V8 JavaScriptエンジンのスタンドアローンの実装を。
  2. 上記のプログラムファイルをファイル名「bfCodes.js」を使用して作業ディレクトリに保存します(Windowsファイル名は大文字と小文字を区別しません)。
  3. 作業ディレクトリを右クリックし、ターゲットを使用してコマンドシェルプログラム(オールディーズのDOSボックス)へのショートカットを作成します。 cmd.exe
  4. ショートカットのプロパティを編集し、作業フォルダーを作業ディレクトリの名前に設定します(ロケーションバーをクリックしてコピーします)。
  5. cmd.exeショートカットを使用して開き、DOSプロンプトが作業ディレクトリで始まることを確認します
  6. 「node bfCodes」を引用符なしで入力して入力します-最初に実行するノードは、再度実行するよりも時間がかかる場合があります。
  7. 「node bfCodes 16」と入力して、最大16個のコードを表示します。大きな数字を使用しないでください。

最適化

アルゴリズムは、長さ1のコード文字列で始まるすべての機能文字の組み合わせを循環します。これは、最下位桁から15オドメーターを回転させると考えてください。高次桁は、速度が遅くなるにつれてクリックオーバーします。bfCodes実行速度を最適化するために、スタックの長さをゼロまたは負にする、またはスタックに複数の数字を残す生成コードを評価しません。

ブルートフォース問題

15文字のコードセットの場合、指定された長さのすべての組み合わせを実行するのにかかる時間は、

T len = 15 * T len-1

つまり、プログラムが私のプログラムよりも15倍高速に実行される場合、同時に1つの追加の文字コード文字列のみをチェックできます。さらに2つの文字を同時にチェックするには、プログラムを225倍高速に実行する必要があります。ブルートフォースアプローチにかかる時間は、コード文字列の長さが長くなるにつれて指数関数的に長くなります。そして、数値の大きさは、それを生成するために必要なbefungeバイトの数を必然的に示します。

いくつかの数字。

整数までのWindows 7 32ビットメモ帳でコードのリストを生成するおよその時間

  • 9:1ミリ秒
  • 10:16ミリ秒
  • 32:156ミリ秒
  • 81:312ミリ秒
  • 93:18.5秒
  • 132:28秒

3727(66の2乗+ 6)のベファンジを生成するには、1時間47分かかり、生成されました 578*+:*6+

最適なコード生成

最短の長さをチェックせずに数値のベファンジを生成するのは比較的簡単です。整数の平方根と剰余を使用した再帰アルゴリズムを使用すると、最大132の数値のエンコードに28秒ではなく約3ミリ秒かかりました。それらは最適ではありませんでした。動作方法により、この特定のアルゴリズム638:*-:*+は3727に対して約1ミリ秒(1時間程度ではなく)で生成されましたがたまたま最適でした。

非ブルートフォース法を提供することの問題は、それがあらゆる場合に最適であることを証明しています。幸運を!


あなたは、あなたの文字列がで有効な評価ツリーを表現しなければならないことを観察することによってたくさんして指数を下げることができる必要があり+-*/、内側のノードでと0-9:葉で(と:左端にはできません)。したがって、ステップn(nは0から始まります)でサイズ2 * n + 1のすべての有効なツリーを生成および評価し、必要に応じて文字列に変換します
Ton Hospel

3727は61の2乗+ 6ではなく66 :)
ティムヴァーミューレン

1

JavaScript

JSスニペットで何ができますか?私のマシンでは、Firefox 64ビット、60秒で416

function go() {
    B.disabled=true
    O.textContent = '...wait...'
    setTimeout(run, 100)
}

function run()
{
	var o=[0],	
	t0=performance.now(),	
	te=t0+T.value*1000,
	k=[],t=[...'0123456789'],i=0,n=0,e,v,j,l,x,h
	MainLoop:
	for(;;)
	{
	  for(;!k[n] && (e=t[i++]);) 
	  {
	    if(performance.now()>te)break MainLoop
	    
	    for(v=[],j=0;x=e[j++];l=x)
	      1/x?h=v.push(+x):(b=v.pop(),x>'9'?h=v.push(b,b):(a=v.pop(),h=v.push(x<'+'?a*b:x<'-'?a+b:x<'/'?a-b:a/b|0)))
	    if(!k[v])
	    {
	      k[v]=e
	      //if(!e[10])
	      {
	        if (l==':')
	          t.push(e+'+',e+'*')
	        else if (h>1)
	        {
	          if (l == '1') t.push(e+'+',e+'-')
	          else if (l != '0') t.push(e+'+',e+'-',e+'*',e+'/')
	        }  
	        if (h<4)
	        {
	          if (l<'0'|l>'9') t.push(e+':');
	          [...'0123456789'].forEach(x => t.push(e+x))
	        }
	      }  
	    }
	  }
	  o.push([n,k[n]])
    ++n;
	}  
	o[0]='Run time sec '+(performance.now()-t0)/1000+'\nTried '+t.length+'\nRange 0..'+(n-1)+'\nTop '+k.pop()+' '+k.length
	O.textContent=o.join`\n`
    B.disabled=false
}
Time limit sec:<input id=T type=number value=60><button id=B onclick='go()'>GO</button>
<pre id=O></pre>

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.