একটি সংখ্যা প্রকাশ করুন - একটি আধুনিক "ডেস শিফ্রেস এট দেস লেট্রেস"


16

একটি সংখ্যা প্রকাশ করুন

ষাটের দশকে ফরাসী টিভি গেম শো "ডেস শিফ্রেস এট ডেস লেট্রেস" (ডিজিটস অ্যান্ড লেটারস) আবিষ্কার করেছিল। শোটির ডিজিটস-অংশের লক্ষ্যটি ছিল কিছু আধা-এলোমেলোভাবে নির্বাচিত সংখ্যা ব্যবহার করে নির্দিষ্ট 3-অঙ্কের লক্ষ্য সংখ্যায় যতটা সম্ভব আপনি কাছে আসা। প্রতিযোগীরা নিম্নলিখিত অপারেটরগুলি ব্যবহার করতে পারবেন:

  • উপসংহার (1 এবং 2 12)
  • সংযোজন (1 + 2 হয় 3)
  • বিয়োগ (5 - 3 = 2)
  • বিভাগ (8/2 = 4); ফলাফলটি যদি প্রাকৃতিক সংখ্যা হয় তবে বিভাগের অনুমতি রয়েছে
  • গুণ (2 * 3 = 6)
  • ক্রিয়াকলাপগুলি, ক্রিয়াকলাপগুলির নিয়মিত নজরে ওভাররাইড করতে: 2 * (3 + 4) = 14

প্রতিটি প্রদত্ত নম্বর কেবল একবার ব্যবহার করা যেতে পারে বা মোটেও নয়।

উদাহরণস্বরূপ, 728 নম্বরটি সংখ্যার সাথে হুবহু মিলে যায়: 6, 10, 25, 75, 5 এবং 50 নিম্নলিখিত প্রকাশের সাথে:

75 * 10 - ( ( 6 + 5 ) * ( 50 / 25 ) ) = 750 - ( 11 * 2 ) = 750 - 22 = 728

মূল ফ্রেস থেকে এখনও "ডেস শিফ্রেস এট ডেস লেট্রেস"

এই কোড চ্যালেঞ্জে, আপনাকে নির্দিষ্ট লক্ষ্য সংখ্যার যতটা সম্ভব কাছাকাছি একটি অভিব্যক্তি খুঁজে বের করার কাজটি দেওয়া হবে। যেহেতু আমরা একবিংশ শতাব্দীতে বাস করছি, আমরা 60 এর দশকের তুলনায় আরও বড় লক্ষ্য সংখ্যা এবং আরও সংখ্যার সাথে কাজ করার জন্য প্রবর্তন করব।

বিধি

  • অনুমোদিত অপারেটর: কনটেনটেশন, +, -, /, *, (এবং)
  • কনকনেটেশন অপারেটরের কোনও চিহ্ন নেই। শুধু সংখ্যাগুলি একত্রিত করুন।
  • কোনও "বিপরীতমুখী কনটেন্টেশন" নেই। 69 হ'ল 69 এবং এটি 6 এবং 9 তে বিভক্ত হতে পারে না।
  • লক্ষ্য নম্বরটি একটি ধনাত্মক পূর্ণসংখ্যার এবং সর্বাধিক 18 সংখ্যা রয়েছে।
  • কাজ করার জন্য কমপক্ষে দুটি সংখ্যা এবং সর্বাধিক 99 নম্বর রয়েছে। এই সংখ্যাগুলি সর্বাধিক 18 সংখ্যার সাথে ধনাত্মক পূর্ণসংখ্যাও।
  • এটি সম্ভব (আসলে বেশ সম্ভবত) যে সংখ্যার সংখ্যা এবং অপারেটরগুলির ক্ষেত্রে লক্ষ্য সংখ্যাটি প্রকাশ করা যায় না। লক্ষ্যটি যতটা সম্ভব বন্ধ করা।
  • প্রোগ্রামটি একটি যুক্তিসঙ্গত সময়ে শেষ করা উচিত (একটি আধুনিক ডেস্কটপ পিসিতে কয়েক মিনিট)।
  • স্ট্যান্ডার্ড লুফোলস প্রযোজ্য।
  • আপনার প্রোগ্রামটি এই ধাঁধার "স্কোরিং" বিভাগে পরীক্ষার সেটটির জন্য অনুকূলিত করা যাবে না । আমি যদি এই নিয়ম লঙ্ঘন করে সন্দেহ করি তবে আমি পরীক্ষা সেটটি পরিবর্তন করার অধিকার সংরক্ষণ করি।
  • এই না একটি codegolf।

ইনপুট

ইনপুটটিতে এমন একটি সংখ্যার অ্যারে থাকে যা কোনও সুবিধাজনক উপায়ে ফর্ম্যাট করা যায়। প্রথম সংখ্যাটি লক্ষ্য নম্বর। বাকি সংখ্যাগুলি সেই সংখ্যা যা আপনার সাথে লক্ষ্য নম্বরটি গঠনের জন্য কাজ করা উচিত।

আউটপুট

আউটপুট জন্য প্রয়োজনীয়তা হয়:

  • এটি এমন একটি স্ট্রিং হওয়া উচিত যা এতে অন্তর্ভুক্ত:
    • ইনপুট সংখ্যাগুলির কোনও উপসেট (লক্ষ্য সংখ্যা বাদে)
    • অপারেটর সংখ্যা
  • আমি ফাঁকা জায়গা ছাড়াই আউটপুটটিকে একক লাইন হিসাবে পছন্দ করি, তবে যদি আপনার অবশ্যই হয় তবে আপনি উপযুক্ত হিসাবে দেখতে ফাঁকা স্থান এবং নিউলাইনগুলি যুক্ত করতে পারেন। কন্ট্রোলিং প্রোগ্রামে এগুলি উপেক্ষা করা হবে।
  • আউটপুটটি একটি বৈধ গাণিতিক এক্সপ্রেশন হওয়া উচিত।

উদাহরণ

পাঠযোগ্যতার জন্য, এই সমস্ত উদাহরণগুলির একটি সঠিক সমাধান রয়েছে এবং প্রতিটি ইনপুট নম্বর ঠিক একবার ব্যবহার করা হয়।

ইনপুট: 1515483, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
আউটপুট:111*111*(111+11+1)

ইনপুট: 153135, 1, 2, 3, 4, 5, 6, 7, 8, 9
আউটপুট:123*(456+789)

ইনপুট: 8888888888, 9, 9, 9, 99, 99, 99, 999, 999, 999, 9999, 9999, 9999, 99999, 99999, 99999, 1
আউটপুট:9*99*999*9999-9999999-999999-99999-99999-99999-9999-999-9-1

ইনপুট: 207901, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0
আউটপুট:1+2*(3+4)*(5+6)*(7+8)*90

ইনপুট: 34943, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 আউটপুট: 1+2*(3+4*(5+6*(7+8*90))) তবে বৈধ আউটপুটটি হ'ল:34957-6-8

স্কোরিং

একটি প্রোগ্রামের পেনাল্টি স্কোরটি নীচের টেস্টসেটের জন্য প্রকাশের আপেক্ষিক ত্রুটির যোগফল।

স্কোরিং সমীকরণ

উদাহরণস্বরূপ, যদি লক্ষ্য মান 125 হয় এবং আপনার অভিব্যক্তি 120 দেয়, তবে আপনার পেনাল্টি স্কোর অ্যাবস (1 - 120/125) = 0,04।

সর্বনিম্ন স্কোর (সর্বনিম্ন মোট আপেক্ষিক ত্রুটি) সহ প্রোগ্রামটি জয়ী। যদি দুটি প্রোগ্রাম সমানভাবে শেষ হয়, তবে প্রথম জমাটি জিতবে।

শেষ অবধি, টেস্টসেট (৮ টি কেস):

14142, 10, 11, 12, 13, 14, 15
48077691, 6, 9, 66, 69, 666, 669, 696, 699, 966, 969, 996, 999
333723173, 3, 3, 3, 33, 333, 3333, 33333, 333333, 3333333, 33333333, 333333333
589637567, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
8067171096, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199
78649377055, 0, 2, 6, 12, 20, 30, 42, 56, 72, 90, 110, 132, 156, 182, 210, 240, 272, 306, 342, 380, 420, 462, 506, 552, 600, 650, 702, 756, 812, 870, 930, 992
792787123866, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169
2423473942768, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 2000000, 5000000, 10000000, 20000000, 50000000

পূর্ববর্তী অনুরূপ ধাঁধা

এই ধাঁধাটি তৈরি করে এবং এটি স্যান্ডবক্সে পোস্ট করার পরে, আমি পূর্ববর্তী দুটি ধাঁধাতে অনুরূপ কিছু (তবে একই নয়!) লক্ষ্য করেছি: এখানে (কোনও সমাধান নেই) এবং এখানে । এই ধাঁধাটি কিছুটা পৃথক, কারণ এটি কনটেনটেশন অপারেটরের সাথে পরিচয় করিয়ে দেয়, আমি সন্ধান করতে চাই না এবং সঠিক মিল চাই না এবং আমি নিষ্ঠুর বল ছাড়াই সমাধানের কাছাকাছি আসার কৌশলগুলি দেখতে চাই। আমি মনে করি এটি চ্যালেঞ্জিং।


6
আপনি অন্যান্য অপারেটরের ফলাফল একত্রিত করতে পারেন? উদাহরণস্বরূপ, 21 = (1 + 1) 1.
অ্যান্ড্রু

1
কি দারুন. ভাল প্রশ্ন. সেটার কথা ভাবি নি। আমার প্রথম প্রতিক্রিয়া ছিল "কোনও উপায় নয়; আমি এটি কীভাবে চাইছিলাম তা নয়"। তবে এটি এত যুক্তিসঙ্গত। এবং যদি এটি সম্ভব না হয় তবে কনকেন্টেশন কোনও অপারেটরের বেশি হবে না। তাই হ্যাঁ! এটা সম্ভব. একটি অভিব্যক্তির চারপাশে প্রথম বন্ধনী রাখুন, এর পাশে অন্য একটি ভাব বা সংখ্যা রাখুন এবং সমবেত রয়েছে। সুতরাং (1 + 1) (1 + 1) 22 হয়। আমি সেই অনুসারে প্রশ্নটি সামঞ্জস্য করব।
এখন পর্যন্ত

1
আমি যখন ছোট ছিলাম তখন আমি এই শোটি দেখতাম এবং আমি নিশ্চিত যে কনকনেটেশন অপারেটরটি ছিল না । ঠিক আছে, 90-এর দশক থেকেই নিয়মগুলি পরিবর্তন হয়েছে ...
মাইকেল এম।

সম্ভবত আপনি ঠিক বলেছেন। আমি এটি সম্পর্কে নিশ্চিত ছিল না। তবে এটি ধাঁধাটিকে আরও আকর্ষণীয় করে তোলে ...
পর্যন্ত

1
আমি নিশ্চিত করলাম যে উপসংহারটি বিদ্যমান নেই বা এটি একটি সাম্প্রতিক সংযোজন, তবে আমি কেবল এটি পছন্দ করি - চ্যালেঞ্জটিকে আরও আকর্ষণীয় করে তোলে!
ডক্টর

উত্তর:


5

সি ++ 17, স্কোর .0086

থ্রেড রেসের কারণে এই প্রোগ্রামটিতে অ-নিয়ন্ত্রক পেনাল্টি স্কোর রয়েছে, তাই আমি গড়ে তিন রানের উপর ভিত্তি করে ঘোষণা করছি, যার প্রতিটি পরীক্ষা স্যুটকে এক মিনিটের মধ্যে পরিচালনা করে:

score 0.000071 for 14(11*13) = 14143
score 0.000019 for (696699+66)*69 = 48076785
score 0.000069 for 333333+333333333+33333 = 333699999
score 0.000975 for 5(1((((555555255-1-1-4-5-5-5-5-4-4-4-4-4-4-4-4-4-4-4-4-4-5-3-3-3-3-3-3-3-3-3-3-3-3-3-5)/2*3/2-2)/2*3+2+1+1+1+1-1-1)/2*2/2/2/2)/2) = 589062470
score 0.000462 for (((199181197*41-193-191-179-173-167-163-157-151-149-139-137-131-127-113-109-107-103-101-97-89-83-79-73-71-67-61-59-53-47-43-17-3)/5*7+23)/2/11*13+19)/31*37 = 8063447296
score 0.000118 for (992930870*72+812+756+702+650+600+552+506+462+420+380+342-42-56-182-12-210-156-90-20-272-30-6-306)/240*132*2 = 78640130184
score 0.000512 for (((317811*832040*3-39088169-24157817-14930352-9227465-5702887-2178309-1346269-3524578-514229-196418-121393-17711-233-75025-46368-89-28657-4181-10946-6765-34-987-2584-13-610-8-1)/2-377-144)/5-1597)1 = 793193194211
score 0.005725 for 2(20((120000000*20000+50000000+10000000+5000000+2000000+100000+50000+10000+5000+2000-500-1000)/50)/5)+200+100+10 = 2409600268972
total score = .007951

real    0m57.876s
user    4m24.396s
sys     0m0.684s

score 0.000071 for 14(11*13) = 14143
score 0.000019 for (696699+66)*69 = 48076785
score 0.000069 for 333333+333333333+33333 = 333699999
score 0.001675 for (3((((((((555555455+5+5+5+5-1-1-4-4-4-4-4-4-4-4-4-1-4-4-4-4-5-3-3-3-3-3-4)/2*3/2-1)*2+5)/3*3+3)/2-3-3)/2*3/2*2+2)/2*2/2*3+2+1)/5/2)-1-1-1-1-1-1-1-1-1-2)/2*3 = 590624943
score 0.000973 for ((199181197*41-193-191-179-173-167-163-157-151-149-139-137-131-127-113-107-101-59-97-79-3-71-67-83-2-47-37-73-89-103-19-11-29)/5*7+109-23)/61*43 = 8059325224
score 0.000118 for ((992930870*72+812+756+702+650+600+552+506+462+420+380+342+306+272+240+210+182-0-56-110-20-90)/2-42-156)/30*132/12*6 = 78640132296
score 0.000512 for (((317811*832040*3-39088169-24157817-14930352-9227465-5702887-3524578-514229-196418-2178309-1346269-121393-75025-28657-10946-233-46368-89-17711-2584-6765-610-4181-34-987-55-1)/2-8-144-377)/5-1597)1 = 793193194161
score 0.004734 for 2(20((120000000*20000+50000000+10000000+5000000+2000000+100000+50000+10000+5000+2000-100-1000-500)/200*50/10)/5) = 2412000335827
total score = .008171

real    0m45.636s
user    3m30.272s
sys     0m0.720s

score 0.000071 for 14(11*13) = 14143
score 0.000019 for (696699+66)*69 = 48076785
score 0.000069 for 333333+333333333+33333 = 333699999
score 0.002963 for 1(((((((555555555+5+5+5+5+5+5+4+4+4+4-1-2-4-4-4-4-4-4-4-4-4-4-4-3-3-3-3)/2*3+3+2)/2*2+3+3)/2*2/2/2*3+3)/2-3-3)*3/2-1-3)/2*3/2/2)/2 = 587890622
score 0.000069 for ((((199181197*41-193-191-179-173-167-163-157-151-149-139-137-131-127-113-109-107-103-101-97-89-83-79-73-71-67-61-59-53-47-43-37-11)/7)2+3)/23*17-13-5)/31*29 = 8066615553
score 0.000118 for ((992930870*72+812+756+702+650+600+552+506+462+420+380-0-6-90-56-42-272-182-110-210-342-30-306)*2+12)/240*132 = 78640129524
score 0.000512 for (((317811*832040*3-39088169-24157817-14930352-9227465-5702887-2178309-1346269-3524578-514229-196418-121393-75025-46368-28657-144-55-17711-2584-10946-4181-6765-21-610-987-377-8-1)/2-89-13)/5-233-1597)1 = 793193192491
score 0.005725 for 2(20((120000000*20000+50000000+10000000+5000000+2000000+100000+50000+10000+5000+2000-500-1000)/50)/5)+200+100+10 = 2409600268972
total score = .009546

real    0m57.289s
user    4m19.488s
sys     0m0.708s

প্রোগ্রামটি এখানে; ব্যাখ্যা মন্তব্য দেওয়া হয়। আপনি সংজ্ঞা দিতে পারেনCONCAT_NONE traditionalতিহ্যবাহী কাউন্টডাউন বিধিগুলির জন্যCONCAT_DIGITS দিতে পারেন যা সংবিধানের অনুমতি দেয় না, বা ইনপুট মানগুলিকে সংবিধানের অনুমতি দেয় তবে কোনও মধ্যবর্তী ফলাফলের জন্য নয়। ডিফল্টরূপে, উভয়ই সংজ্ঞায়িত না করে সর্বাধিক উদার বিধি ব্যবহৃত হয়।

#include <omp.h>

#include <algorithm>
#include <cmath>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>

// We apply some principles to help us arrive at a good enough solution
// in a reasonable time:

// 1. Ruthlessly prune duplicate expressions from the candidate
//    list.  If we've seen a+b, then there's no need to consider
//    b+a.  Similarly, having seen (a+b)+c, then (a+c)+b can be
//    discounted.
// 2. Detect duplicates by storing batches of part-processed results
//    in sets before sending to the next pass.
// 3. Sort our candidates so that those containing a term near to the
//    target are first in line for further processing.
// 4. Gradually widen our acceptance margin as we proceed.  This
//    allows us to terminate quickly without exhaustively searching
//    the full problem space.
// 5. Parallelize the generation of candidate solutions using OpenMP.

// Define precedence values for our operators, so that we can print
// with the minimum sufficient parentheses.  The values are grouped
// into tens so that add/10 == subtract/10 and mult/10 == divide/10 -
// the operators use that for avoiding duplicate expressions.
static const int PREC_ADD = 26;
static const int PREC_SUBTRACT = 24;
static const int PREC_MULT = 16;
static const int PREC_DIVIDE = 14;
static const int PREC_CONCAT = 2;
static const int PREC_LITERAL = 0;

static const int PREC_MAX = 1000;

class LiteralTerm;

struct Term
{
    long value;
    int precedence;

    Term(long value, int precedence)
        : value(value), precedence(precedence)
    {}
    Term(const Term&) = default;
    virtual ~Term() = default;

    virtual std::string to_string(int p = PREC_MAX) const = 0;
    virtual LiteralTerm as_literal() const = 0;

    long distance(long target) const { return std::abs(value - target); }

    // We sort large values first, in the hope that this will approach
    // the target faster.
    bool operator<(const Term& o) const { return value > o.value; }
};


// We have two kinds of Term: a LiteralTerm is a leaf node of the
// expression tree, and a BinaryTerm is an internal node.
struct Operator;

class LiteralTerm : public Term
{
    std::string s;
public:
    LiteralTerm(std::string s) : Term(std::stol(s), 0), s(s) {}
    LiteralTerm(std::string s, long value) : Term(value, 0), s(s) {}
    std::string to_string(int = PREC_MAX) const override { return s; }
    LiteralTerm as_literal() const override { return *this; }
};

struct BinaryTerm : public Term
{
    Operator const *op;

    std::shared_ptr<const Term> a;
    std::shared_ptr<const Term> b;

    BinaryTerm(long value, const Operator* op, std::shared_ptr<const Term> a, std::shared_ptr<const Term> b);
    BinaryTerm(const BinaryTerm&) = default;
    BinaryTerm& operator=(const BinaryTerm&) = default;

    std::string to_string(int p = PREC_MAX) const;

    LiteralTerm as_literal() const override { return { to_string(), value }; }
};

struct TermList {
    std::vector<std::shared_ptr<const Term>> terms;
    std::vector<long> values;
    long target_value;
    long badness;

    TermList(std::vector<std::shared_ptr<const Term>> terms, long target_value)
        : terms(std::move(terms)),
          values(),
          target_value(target_value),
          badness(min_badness(this->terms, target_value))
    {
        values.reserve(terms.size());
        std::transform(terms.begin(), terms.end(),
                       std::back_inserter(values), [](auto t) { return t->value; });
        // Literals that begin with "0" need to be distinct from (but
        // adjacent to) equivalent non-literals.  Append a negative
        // value for each term with leading zeros.  There's an edge
        // case involving multiple leading zeros, but we'll ignore
        // that.
        for (const auto& v: terms)
            if (v->precedence <= PREC_CONCAT && v->value > 0 && v->to_string()[0] == '0')
                values.push_back(-v->value);
    }

    // Sort according to the term that's nearest to the target.
    bool operator<(const TermList& o) const
    {
        return std::make_tuple(std::cref(badness),   std::cref(values))
            <  std::make_tuple(std::cref(o.badness), std::cref(o.values));
    }

private:
    static long min_badness(const std::vector<std::shared_ptr<const Term>>& t, long target_value)
    {
        auto less_bad = [target_value](const auto& a, const auto&b)
            { return a->distance(target_value) < b->distance(target_value); };
        auto const& e = *std::min_element(t.begin(), t.end(), less_bad);
        return std::abs(e->value - target_value);
    }
};

using Set = std::set<TermList>;

// Detect duplicate expressions.  This will discount "3+2-3", "8*5*2/3/5"
// and similar expressions that contain simple pairs of inverse operands.
static bool contains_value(const Term& t, int precedence, long value)
{
    auto *const b = dynamic_cast<const BinaryTerm*>(&t);
    if (t.precedence == precedence)
        return t.value == value
            || b && b->b->value < value
            || b && contains_value(*b->a, precedence, value)
            || b && contains_value(*b->b, precedence, value);
    if (t.precedence/10 == precedence/10)
        // Advance through the subtractions to inspect the additions
        // (or through the divides to inspect the multiplications).
        return b && contains_value(*b->a, precedence, value);
    return false;
}

// An Operator is a factory producing binary terms of a given type,
// and for printing those terms.  Here's the abstract base class.
struct Operator
{
    using TermPointer = std::shared_ptr<const Term>;
    using BinaryTermPointer = std::shared_ptr<const BinaryTerm>;

    int const precedence;
    std::string const joiner;

    virtual std::string to_string(const Term &a, const Term &b) const {
        return a.to_string(precedence) + joiner + b.to_string(precedence);
    }

    virtual BinaryTermPointer make_term(TermPointer a, TermPointer b) const {
        long r = evaluate(*a, *b);
        return r ? std::make_shared<BinaryTerm>(r, this, a, b) : BinaryTermPointer();
    }

    virtual ~Operator() = default;

protected:
    Operator(int precedence, std::string joiner) : precedence(precedence), joiner(joiner) {}

    virtual long evaluate(const Term& a, const Term& b) const = 0;
};

// Now we define a subclass for each permitted operator
struct AddOperator : Operator
{
    AddOperator() : Operator(PREC_ADD, "+") {}

    long evaluate(const Term& a, const Term& b) const override
    {
        const auto *d = dynamic_cast<const BinaryTerm*>(&a);
        long r;
        return b.precedence/10 != PREC_ADD/10
            && a.precedence != PREC_SUBTRACT
            && b.value > 0
            && ! (d && d->precedence == this->precedence && d->b->value < b.value)
            && !__builtin_add_overflow(a.value, b.value, &r)
            ? r : 0;
    }
};
struct SubtractOperator : Operator
{
    SubtractOperator() : Operator(PREC_SUBTRACT, "-") {}

    long evaluate(const Term& a, const Term& b) const override
    {
        return b.precedence < PREC_SUBTRACT
            && a.value > b.value
            && !contains_value(a, PREC_ADD, b.value)
            ? a.value - b.value : 0;
    }
};
struct MultiplyOperator : Operator
{
    MultiplyOperator() : Operator(PREC_MULT, "*") {}

    long evaluate(const Term& a, const Term& b) const override
    {
        const auto *d = dynamic_cast<const BinaryTerm*>(&a);
        long r;
        return b.precedence/10 != PREC_MULT/10
            && b.value > 1
            && (b.value > 2 || a.value > 2)
            && ! (d && d->precedence == this->precedence && d->b->value < b.value)
            && !__builtin_mul_overflow(a.value, b.value, &r)
            ? r : 0;
    }
};
struct DivideOperator : Operator
{
    DivideOperator() : Operator(PREC_DIVIDE, "/") {}

    long evaluate(const Term& a, const Term& b) const override
    {
        return b.precedence/10 != PREC_DIVIDE/10 && b.value > 1
            && a.value % b.value == 0
            && !contains_value(a, PREC_MULT, b.value)
            ? a.value / b.value : 0;
    }
};

struct ConcatOperator : Operator
{
    ConcatOperator() : Operator(PREC_CONCAT, "") {}

    long evaluate(const Term& a, const Term& b) const override
    {
#ifdef CONCAT_DIGITS
        if (a.precedence > PREC_CONCAT || a.value == 0 || b.precedence >= PREC_CONCAT)
            return 0;
#else  // CONCAT_FULL
        if (b.precedence == PREC_CONCAT || a.value == 0)
            return 0;
#endif
        long bv = b.value, av = a.value, x = 1, r;
        if (b.precedence > PREC_CONCAT) while (x <= bv) x*= 10;
        else { int d = b.to_string().length(); while (d--) x*= 10; }
        return __builtin_mul_overflow(av, x, &r) || __builtin_add_overflow(r, bv, &r) ? 0 : r;
    }
};
struct ReverseConcatOperator : ConcatOperator
{
    BinaryTermPointer make_term(TermPointer a, TermPointer b) const override
    {
        return ConcatOperator::make_term(b, a);
    }
};

static const std::vector<std::shared_ptr<const Operator>> ops{
#ifndef CONCAT_NONE
        std::make_shared<ConcatOperator>(),
        std::make_shared<ReverseConcatOperator>(),
#endif
        std::make_shared<MultiplyOperator>(),
        std::make_shared<AddOperator>(),
        std::make_shared<SubtractOperator>(),
        std::make_shared<DivideOperator>(),
};


// Implement the BinaryTerm members that use Operator
BinaryTerm::BinaryTerm(long value, const Operator* op, std::shared_ptr<const Term> a, std::shared_ptr<const Term> b)
    : Term(value, op->precedence), op(op), a(std::move(a)), b(std::move(b))
{}

std::string BinaryTerm::to_string(int p) const
{
    auto const s = op->to_string(*a, *b);
    return (p/10) < (precedence/10) ? "("+s+")" : s;
}


// An object to represent our target value, and how close we have
// reached so far.
struct Target
{
    const long value;
    double max_badness = 0;

    LiteralTerm best = {"0"};
    long best_badness = value;

    bool done() const { return best_badness < max_badness; }
    double score() const { return 1.*best_badness/value; }

    void update(const Term& t)
    {
        auto badness = std::abs(t.value - value);
        if (badness < best_badness) {
            best = t.as_literal();
            best_badness = badness;
        }
    }

    void update(const TermList& terms)
    {
        for (auto t: terms.terms)
            update(*t);
    }

    void increase_threshold(size_t items_seen)
    {
        // Adjust our acceptance threshold nearer to accepting 0 by
        // 0.01% for every million values seen.
        max_badness += (value - max_badness) * .0001 * std::exp(items_seen / 1000000);
    }
};

// OpenMP reduction for sets
auto merge(auto& a, auto& b)
{
    auto it = a.begin();
    for (auto&& e: b)
        it = a.insert(std::move(e)).first;
    return a;
}
#pragma omp declare reduction(merge: Set: merge<Set>(omp_out, omp_in) ) \
    initializer(omp_priv = Set())


// We run a cascade of pair-wise combination steps, where for each
// input TermList, we generate every possible allowed pairing of its
// terms, and pass that through (in batches) to the next stage.
struct Combiner
{
    std::unique_ptr<Combiner> const next;
    Target& target;
    size_t const max_output_size;
    size_t const nterms;

    Set input = {};
    size_t output_size = 0;

    Combiner(Target& target, size_t nterms, size_t max_output_size)
        : next(nterms > 0 ? std::make_unique<Combiner>(target, nterms-1, max_output_size) : nullptr),
          target(target),
          max_output_size(max_output_size),
          nterms(nterms)
    {}

    inline void insert(const TermList&& t)
    {
        target.update(t);
        if (target.done()) return;
        if (next) {
            if (input.insert(t).second)
                output_size += count_distinct_pairs(t);
            if (output_size >= max_output_size)
                process_input();
        }
    }

    void finish()
    {
        process_input();
        if (next)
            next->finish();
    }

private:
    // Here's where we do the real work - generating and sifting the
    // combined terms for the next pass.
    void process_input()
    {
        if (target.done()) {
            return;
        }

        if (!next)
            return;

        // Move the elements into a vector, so we can parallelize the
        // for-loop.
        auto in = std::vector<Set::value_type>();
        in.reserve(input.size());
        std::move(input.begin(), input.end(), std::back_inserter(in));
        input.clear();
        output_size = 0;

        auto out = Set();

#pragma omp parallel reduction(merge:out)
        {
#pragma omp for
            for (auto it = in.begin();  it < in.end();  ++it)
            {
                try {
                    const auto end = it->terms.cend();
                    for (auto i = it->terms.cbegin();  i != end;  i = std::upper_bound(i, end, *i))
                        for (auto j = i+1;  j != end;  j = std::upper_bound(j, end, *j)) {
                            for (const auto& op: ops) {
                                auto x = op->make_term(*i, *j);
                                if (x) out.insert(replace(*it, i, j, x));
                            }
                        }
                } catch (const std::bad_alloc&) {
                    // Ignore it; process what we've generated so far.
                }
            }
        }

        // Now we're in single-threaded code, we can pass the combined
        // results to the next combiner.
        for (auto& o: out)
            next->insert(std::move(o));

        target.increase_threshold(out.size());
    }


    // Helper methods used by the above

    // An upper bound on the possible number of output TermLists,
    // assuming every combination is valid.  If all n terms in the
    // input list are distinct, that's just ½n(n-1), but if values
    // are duplicated, we need to reduce n to the number of distinct
    // values, and then add in the cases where we pick two of the
    // same value.
    static int count_distinct_pairs(const TermList& terms)
    {
        int distinct = 0, duplicated = 0;
        auto it = terms.terms.begin(),
            end = terms.terms.end();
        while (it != end) {
            ++distinct;
            auto const& v = (*it)->value;
            if (++it == end || (*it)->value != v) continue;
            ++duplicated;
            while (++it != end && (*it)->value == v)
                ;
        }
        return distinct * (distinct - 1) / 2 + duplicated;
    }

    // Create a new TermList from o by replacing elements i and j with
    // newly-created term n.
    static TermList replace(const TermList& o, auto i, auto j, std::shared_ptr<const Term> n)
    {
        std::vector<std::shared_ptr<const Term>> r;
        r.reserve(o.terms.size() - 1);
        auto added = false;
        for (auto k = o.terms.begin();  k != o.terms.end();  ++k) {
            if (!added && (*k)->value < n->value) { r.push_back(n); added = true; }
            if (k != i && k != j) r.push_back(*k);
        }
        if (!added) r.push_back(n);
        return { r, o.target_value };
    }
};


#include <iostream>
std::ostream& operator<<(std::ostream& o, const Term& t)
{
    return o << t.to_string()<< " = " << t.value;
}
std::ostream& operator<<(std::ostream& o, const TermList& t)
{
    auto *sep = "";
    o << "[" << t.badness << "] ";
    for (auto const& x: t.terms)
        o << sep << *x, sep = ", ";
    return o;
}

int main(int argc, char **argv)
{
    if (argc < 3) {
        std::cerr << "Usage: " << argv[0] << " target term ...";
        return EXIT_FAILURE;
    }
    auto target = Target{std::stol(*++argv)};

    std::vector<std::shared_ptr<const Term>> terms;
    while (*++argv) {
        auto t = std::make_shared<LiteralTerm>(*argv);
        target.update(*t);
        terms.push_back(t);
    }
    std::sort(terms.begin(), terms.end());

    // Construct the sieve
    Combiner search{target, terms.size(), 2500000/terms.size() + 1}; // tunable - max set size
    search.insert({terms, target.value});
    search.finish();

    std::cout << "score " << std::fixed << target.score() << " for " << target.best << std::endl;
}

আমি জিসিসি 6.2 ব্যবহার করে এটি সংকলিত করেছি g++ -std=c++17 -fopenmp -march=native -O3(কিছু ডিবাগিং এবং সতর্কতা বিকল্প সহ)।


3

পাইথন 2.7। স্কোর: 1,67039106

সুতরাং, আমি নিজেই এটি নিক্ষেপ করার সিদ্ধান্ত নিয়েছি। খুব অভিনব কিছু না। এই প্রোগ্রামটি বিপরীত ক্রমে সংখ্যাগুলি বাছাই করে (বড় থেকে ছোট) এবং সমস্ত অপারেটরকে ক্রমান্বয়ে সংখ্যায় চেষ্টা করে। দ্রুত জ্বলন্ত, তবে এমন একটি পারফরম্যান্স যা ছাড়ার উপযুক্ত।

প্রোগ্রামটি এখানে:

import sys

def score(current,target):
    return abs(1.0-current/float(target))

# Process input and init variables
targetvalue=int(sys.argv[1].strip(','))
numbers=[int(a.strip(',')) for a in sys.argv[2:]]
numbers.sort(reverse=True)
expression='('+str(numbers[0])+')'
currentvalue=nextvalue=testvalue=numbers[0]

# Loop over all values (except the first one)
for value in numbers[1:]:
    # Set multiplication as the reference operator...
    testvalue=currentvalue*value
    minscore=score(testvalue,targetvalue)
    operator="*"
    nextvalue=testvalue

    # then try division (only if result is integer and not divided by zero)...
    if value!=0 and currentvalue%value==0:
        testvalue=currentvalue/value
        if score(testvalue,targetvalue)<minscore:
            operator="/"
            minscore=score(testvalue,targetvalue)
            nextvalue=testvalue

    # and addition...
    testvalue=currentvalue+value
    if score(testvalue,targetvalue)<minscore:
        operator="+"
        minscore=score(testvalue,targetvalue)
        nextvalue=testvalue

    # and subtraction...
    testvalue=currentvalue-value
    if score(testvalue,targetvalue)<minscore:
        operator="-"
        minscore=score(testvalue,targetvalue)
        nextvalue=testvalue

    # and concatenation
    testvalue=int(str(currentvalue)+str(value))
    if score(testvalue,targetvalue)<minscore:
        operator=""
        minscore=score(testvalue,targetvalue)
        nextvalue=testvalue

    # finally check if any operator improces the score. If so, append to the expression
    if score(nextvalue,targetvalue)<score(currentvalue,targetvalue):
        expression='('+expression+operator+str(value)+')'
        currentvalue=nextvalue

print(expression)

সমস্ত পরীক্ষার ক্ষেত্রে আউটপুট হ'ল:

((((((15)14)*13)-12)-11)-10)
((((((((((((999)996)+969)+966)+699)+696)+669)+666)*69)-66)-9)-6)
(((((((((333333333)+333333)+33333)+3333)+333)+33)+3)+3)+3)
(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((5)5)5)5)5)5)5)5)5)+5)+5)+5)+5)+5)+5)+4)+4)+4)+4)+4)+4)+4)+4)+4)+4)+4)+4)+4)+4)+4)+3)+3)+3)+3)+3)+3)+3)+3)+3)+3)+3)+3)+3)+3)+3)+2)+2)+2)+2)+2)+2)+2)+2)+2)+2)+2)+2)+2)+2)+2)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)+1)
((((((((((((((((((((((((((((((((((((((((((((((199)197)193)+191)+181)+179)+173)+167)+163)+157)+151)+149)+139)+137)+131)+127)+113)+109)+107)+103)+101)+97)+89)+83)*79)-73)-71)-67)-61)-59)-53)-47)-43)-41)-37)-31)-29)-23)-19)-17)-13)-11)-7)-5)-3)/2)
(((((((((((((((((((((((((((((((992)930)870)+812)+756)+702)+650)+600)+552)+506)+462)+420)+380)+342)+306)+272)+240)+210)+182)*156)-132)-110)-90)-72)-56)-42)-30)/20)*12)-6)-2)
((((((((((((((((((((((((((((((((((((((39088169)+24157817)+14930352)+9227465)+5702887)+3524578)+2178309)+1346269)+832040)+514229)+317811)+196418)+121393)+75025)+46368)+28657)+17711)*10946)-6765)-4181)-2584)-1597)-987)-610)-377)-233)-144)-89)-55)-34)-21)-13)-8)-5)-3)/2)+1)+1)
(((((((((((((((((((((50000000)+20000000)+10000000)+5000000)+2000000)+100000)*50000)-20000)-10000)-5000)-2000)-1000)-500)-200)-100)-50)-20)-10)/5)*2)+1)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.