সংক্ষিপ্ত সর্বজনীন গোলকধাঁধা প্রস্থান স্ট্রিং


48

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

নোট করুন যে প্রারম্ভিক এবং প্রস্থান দুটিও গোলকধাঁটির বাইরের সীমানায় থাকা দরকার না, সুতরাং এটি একটি বৈধ গোলকধাঁধা:

কেন্দ্রীয় কক্ষে প্রস্থান সহ একটি 3 বাই 3 ধাঁধা

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

দ্বারা অনুপ্রাণিত এই puzzling.SE প্রশ্ন , যার জন্য xnor একটি প্রদত্ত সমাধানের প্রতিপাদ্য পদ্ধতি একটি সঙ্গে খুব একটি একক পংক্তি খুঁজে পেতে পারেন, লেখার কোড দীর্ঘ স্ট্রিংটি প্রস্থানের 3 ধাঁধা করে যে কোনো 3।

অবৈধ মেজেগুলি বাদ দিয়ে (একই ঘরে শুরু করুন এবং প্রস্থান করুন, বা প্রারম্ভিকভাবে পৌঁছনীয় নয় প্রস্থান করুন) এখানে 138,172 টি বৈধ ম্যাজ রয়েছে এবং তাদের প্রতিটি স্ট্রিং অবশ্যই প্রস্থান করতে হবে।

বৈধতা

স্ট্রিং অবশ্যই নিম্নলিখিত সন্তুষ্ট:

  • এটি কেবলমাত্র 'এন', 'ই', 'এস' এবং 'ডাব্লু' অক্ষর দ্বারা রচিত।
  • এটি শুরুতে যদি শুরু হয়, তবে এটি প্রয়োগ করা কোনও ধাঁধা থেকে বেরিয়ে যায়।

যেহেতু সমস্ত সম্ভাব্য ম্যাজগুলির সেটটিতে প্রতিটি সম্ভাব্য বৈধ প্রারম্ভিক পয়েন্টের সাথে প্রতিটি সম্ভাব্য গোলকধাঁধা অন্তর্ভুক্ত রয়েছে, এর স্বয়ংক্রিয়ভাবে অর্থ স্ট্রিংটি কোনও বৈধ প্রারম্ভিক বিন্দু থেকে কোনও গোলকধাঁধা থেকে প্রস্থান করবে । এটি হ'ল যে কোনও প্রারম্ভিক বিন্দু থেকে প্রস্থানটি প্রবেশযোগ্য।

জয়লাভ

বিজয়ী এমন উত্তর যা সবচেয়ে সংক্ষিপ্ত বৈধ স্ট্রিং সরবরাহ করে এবং এটি উত্পাদন করতে ব্যবহৃত কোড অন্তর্ভুক্ত করে। যদি উত্তরগুলির মধ্যে একটিরও বেশি এই সংক্ষিপ্ত দৈর্ঘ্যের একটি স্ট্রিং সরবরাহ করে তবে স্ট্রিংয়ের দৈর্ঘ্যটি জয়ী পোস্টকারী সবার আগে।

উদাহরণ

আপনাকে মারতে কিছু দেওয়ার জন্য এখানে 500 টি অক্ষরের দীর্ঘ স্ট্রিংয়ের একটি উদাহরণ রয়েছে:

SEENSSNESSWNNSNNNNWWNWENENNWEENSESSNENSESWENWWWWWENWNWWSESNSWENNWNWENWSSSNNNNNNESWNEWWWWWNNNSWESSEEWNENWENEENNEEESEENSSEENNWWWNWSWNSSENNNWESSESNWESWEENNWSNWWEEWWESNWEEEWWSSSESEEWWNSSEEEEESSENWWNNSWNENSESSNEESENEWSSNWNSEWEEEWEESWSNNNEWNNWNWSSWEESSSSNESESNENNWEESNWEWSWNSNWNNWENSNSWEWSWWNNWNSENESSNENEWNSSWNNEWSESWENEEENSWWSNNNNSSNENEWSNEEWNWENEEWEESEWEEWSSESSSWNWNNSWNWENWNENWNSWESNWSNSSENENNNWSSENSSSWWNENWWWEWSEWSNSSWNNSEWEWENSWENWSENEENSWEWSEWWSESSWWWNWSSEWSNWSNNWESNSNENNSNEWSNNESNNENWNWNNNEWWEWEE

এটি দানের জন্য orlp ধন্যবাদ ।


লিডারবোর্ড

লিডারবোর্ড

সমান স্কোরগুলি সেই স্কোর পোস্ট করার ক্ষেত্রে তালিকাভুক্ত করা হয়। কোনও উত্তর দেওয়া স্কোর সময়ের সাথে সাথে আপডেট হতে পারে যেহেতু উত্তরগুলি পোস্ট করা আদেশের প্রয়োজন হয় না।


বিচারক

এখানে একটি পাইথন 3 বৈধকারক যা একটি কমান্ড লাইন আর্গুমেন্ট হিসাবে বা এসটিডিএন এর মাধ্যমে NESW এর স্ট্রিং নেয়।

একটি অবৈধ স্ট্রিংয়ের জন্য, এটি আপনাকে ভুলে যাওয়া কোনও গোলকধাঁধার দৃশ্যমান উদাহরণ দেবে।


3
এটি সত্যিই ঝরঝরে প্রশ্ন। একটি সংক্ষিপ্ততম স্ট্রিং (বা বেশ কয়েকটি স্ট্রিং এবং এমন প্রমাণ আছে যে কোনও ছোট উত্তর দেওয়া যায় না)? এবং যদি তাই হয়, আপনি এটি জানেন?
অ্যালেক্স ভ্যান লিউ

1
@ অ্যালেক্সরিং হ্যাঁ শুরুটি 9 টি কোষের যে কোনও একটি হতে পারে এবং প্রস্থানটি 9 টি কোষের যে কোনও একটি হতে পারে, যতক্ষণ না সেগুলি একই কোষ না থাকে এবং প্রস্থান শুরু থেকে পৌঁছনীয়।
ট্রাইকোপলাক্স

1
সামান্য এই Stackoverflow প্রশ্নের অনুরূপ: stackoverflow.com/questions/26910401/... - কিন্তু শুরু করা এবং শেষ সেল উপরের বাম এবং যে এক নীচের অংশে ডানদিকে, যা 2423. সম্ভব ধাঁধা গণনা হ্রাস হয়
schnaader

1
@proudhaskeller যে কোনও উপায়েই একটি বৈধ প্রশ্ন হয়ে উঠবে। N = 3 এর জন্য প্রাপ্ত সাধারণ ক্ষেত্রে আরও সাধারণ কোডের প্রয়োজন হয়। এই নির্দিষ্ট কেসটি অপটিমেশনের জন্য অনুমতি দেয় যা সাধারণ এনগুলিতে প্রযোজ্য নয় এবং আমি এটি জিজ্ঞাসা করার উপায়টি বেছে নিয়েছি।
ট্রাইকোপ্লাক্স

2
কেউ কি এই সমস্যাটির কাছে আসাটিকে নিয়মিত অভিব্যক্তির সবচেয়ে স্বল্পতম স্বীকৃত স্ট্রিং হিসাবে বিবেচনা করছেন? রেজেক্সেসে রূপান্তরিত করার আগে এটির সমস্যার সংখ্যা অনেক কমাতে হবে, তবে তাত্ত্বিকভাবে একটি যাচাইযোগ্য অনুকূল সমাধান খুঁজে পেতে পারে।
কাইল ম্যাককর্মিক

উত্তর:


37

সি ++, 97 95 93 91 91 86 83 82 81 79 টি অক্ষর

NNWSWNNSENESESWSSWNSEENWWNWSSEWWNENWEENWSWNWSSENENWNWNESENESESWNWSESEWWNENWNEES

আমার কৌশলটি মোটামুটি সহজ - একটি বিবর্তন অ্যালগরিদম যা বাড়তে, সঙ্কুচিত করতে, বৈধ ক্রমগুলির উপাদানগুলিকে অদলবদল করতে এবং পরিবর্তন করতে পারে। আমার বিবর্তনের যুক্তিটি এখন @ এসপি 3000 এর মতোই, কারণ আমার ক্ষেত্রে তার উন্নতি হয়েছিল।

যাইহোক, আমার ধাঁধা যুক্তির বাস্তবায়ন বরং নিবিড়। এটি আমাকে ফোস্কা গতিতে স্ট্রিংগুলি বৈধ কিনা তা পরীক্ষা করতে সহায়তা করে। মন্তব্য do_moveএবং Mazeনির্মাণকারীর দ্বারা এটিকে বোঝার চেষ্টা করুন ।

#include <algorithm>
#include <bitset>
#include <cstdint>
#include <iostream>
#include <random>
#include <set>
#include <vector>

/*
    Positions:

        8, 10, 12
        16, 18, 20
        24, 26, 28

    By defining as enum respectively N, W, E, S as 0, 1, 2, 3 we get:

        N: -8, E: 2, S: 8, W: -2
        0: -8, 1: -2, 2: 2, 3: 8

    To get the indices for the walls, average the numbers of the positions it
    would be blocking. This gives the following indices:

        9, 11, 12, 14, 16, 17, 19, 20, 22, 24, 25, 27

    We'll construct a wall mask with a 1 bit for every position that does not
    have a wall. Then if a 1 shifted by the average of the positions AND'd with
    the wall mask is zero, we have hit a wall.
*/

enum { N = -8, W = -2, E = 2, S = 8 };
static const int encoded_pos[] = {8, 10, 12, 16, 18, 20, 24, 26, 28};
static const int wall_idx[] = {9, 11, 12, 14, 16, 17, 19, 20, 22, 24, 25, 27};
static const int move_offsets[] = { N, W, E, S };

int do_move(uint32_t walls, int pos, int move) {
    int idx = pos + move / 2;
    return walls & (1ull << idx) ? pos + move : pos;
}

struct Maze {
    uint32_t walls;
    int start, end;

    Maze(uint32_t maze_id, int start, int end) {
        walls = 0;
        for (int i = 0; i < 12; ++i) {
            if (maze_id & (1 << i)) walls |= 1 << wall_idx[i];
        }
        this->start = encoded_pos[start];
        this->end = encoded_pos[end];
    }

    uint32_t reachable() {
        if (start == end) return false;

        uint32_t reached = 0;
        std::vector<int> fill; fill.reserve(8); fill.push_back(start);
        while (fill.size()) {
            int pos = fill.back(); fill.pop_back();
            if (reached & (1 << pos)) continue;
            reached |= 1 << pos;
            for (int m : move_offsets) fill.push_back(do_move(walls, pos, m));
        }

        return reached;
    }

    bool interesting() {
        uint32_t reached = reachable();
        if (!(reached & (1 << end))) return false;
        if (std::bitset<32>(reached).count() <= 4) return false;

        int max_deg = 0;
        uint32_t ends = 0;
        for (int p = 0; p < 9; ++p) {
            int pos = encoded_pos[p];
            if (reached & (1 << pos)) {
                int deg = 0;
                for (int m : move_offsets) {
                    if (pos != do_move(walls, pos, m)) ++deg;
                }
                if (deg == 1) ends |= 1 << pos;
                max_deg = std::max(deg, max_deg);
            }
        }

        if (max_deg <= 2 && ends != ((1u << start) | (1u << end))) return false;

        return true;
    }
};

std::vector<Maze> gen_valid_mazes() {
    std::vector<Maze> mazes;
    for (int maze_id = 0; maze_id < (1 << 12); maze_id++) {
        for (int points = 0; points < 9*9; ++points) {
            Maze maze(maze_id, points % 9, points / 9);
            if (!maze.interesting()) continue;
            mazes.push_back(maze);
        }
    }

    return mazes;
}

bool is_solution(const std::vector<int>& moves, Maze maze) {
    int pos = maze.start;
    for (auto move : moves) {
        pos = do_move(maze.walls, pos, move);
        if (pos == maze.end) return true;
    }

    return false;
}

std::vector<int> str_to_moves(std::string str) {
    std::vector<int> moves;
    for (auto c : str) {
        switch (c) {
        case 'N': moves.push_back(N); break;
        case 'E': moves.push_back(E); break;
        case 'S': moves.push_back(S); break;
        case 'W': moves.push_back(W); break;
        }
    }

    return moves;
}

std::string moves_to_str(const std::vector<int>& moves) {
    std::string result;
    for (auto move : moves) {
             if (move == N) result += "N";
        else if (move == E) result += "E";
        else if (move == S) result += "S";
        else if (move == W) result += "W";
    }
    return result;
}

bool solves_all(const std::vector<int>& moves, std::vector<Maze>& mazes) {
    for (size_t i = 0; i < mazes.size(); ++i) {
        if (!is_solution(moves, mazes[i])) {
            // Bring failing maze closer to begin.
            std::swap(mazes[i], mazes[i / 2]);
            return false;
        }
    }
    return true;
}

template<class Gen>
int randint(int lo, int hi, Gen& gen) {
    return std::uniform_int_distribution<int>(lo, hi)(gen);
}

template<class Gen>
int randmove(Gen& gen) { return move_offsets[randint(0, 3, gen)]; }

constexpr double mutation_p = 0.35; // Chance to mutate.
constexpr double grow_p = 0.1; // Chance to grow.
constexpr double swap_p = 0.2; // Chance to swap.

int main(int argc, char** argv) {
    std::random_device rnd;
    std::mt19937 rng(rnd());
    std::uniform_real_distribution<double> real;
    std::exponential_distribution<double> exp_big(0.5);
    std::exponential_distribution<double> exp_small(2);

    std::vector<Maze> mazes = gen_valid_mazes();

    std::vector<int> moves;
    while (!solves_all(moves, mazes)) {
        moves.clear();
        for (int m = 0; m < 500; m++) moves.push_back(randmove(rng));
    }

    size_t best_seen = moves.size();
    std::set<std::vector<int>> printed;
    while (true) {
        std::vector<int> new_moves(moves);
        double p = real(rng);

        if (p < grow_p && moves.size() < best_seen + 10) {
            int idx = randint(0, new_moves.size() - 1, rng);
            new_moves.insert(new_moves.begin() + idx, randmove(rng));
        } else if (p < swap_p) {
            int num_swap = std::min<int>(1 + exp_big(rng), new_moves.size()/2);
            for (int i = 0; i < num_swap; ++i) {
                int a = randint(0, new_moves.size() - 1, rng);
                int b = randint(0, new_moves.size() - 1, rng);
                std::swap(new_moves[a], new_moves[b]);
            }
        } else if (p < mutation_p) {
            int num_mut = std::min<int>(1 + exp_big(rng), new_moves.size());
            for (int i = 0; i < num_mut; ++i) {
                int idx = randint(0, new_moves.size() - 1, rng);
                new_moves[idx] = randmove(rng);
            }
        } else {
            int num_shrink = std::min<int>(1 + exp_small(rng), new_moves.size());
            for (int i = 0; i < num_shrink; ++i) {
                int idx = randint(0, new_moves.size() - 1, rng);
                new_moves.erase(new_moves.begin() + idx);
            }
        }

        if (solves_all(new_moves, mazes)) {
            moves = new_moves;

            if (moves.size() <= best_seen && !printed.count(moves)) {
                std::cout << moves.size() << " " << moves_to_str(moves) << "\n";
                if (moves.size() < best_seen) {
                    printed.clear(); best_seen = moves.size();
                }
                printed.insert(moves);
            }
        }
    }

    return 0;
}

5
বৈধ নিশ্চিত করা হয়েছে। আমি মুগ্ধ - আমি এই ছোটটি স্ট্রিংগুলি দেখতে আশা করিনি।
ট্রিকোপল্যাক্স

2
আমি অবশেষে জিসিসি ইনস্টল করে নিজের জন্য এটি চালিয়ে যাচ্ছি। স্ট্রিংগুলি পরিবর্তন এবং ধীরে ধীরে সঙ্কুচিত হওয়া
হিপোথনিক

1
@ থ্রিচোপ্লেক্স আমি আপনাকে বলেছিলাম এটি মজাদার ছিল :)
orlp

2
@ অ্যালেক্সারিংকিং আমি এই উত্তরটি প্রয়োগ করে আমার উত্তর আপডেট করেছি। যদি আপনি বিচ্ছিন্নতার দিকে তাকান তবে আপনি দেখতে পাবেন এটি কোনও শাখা বা লোড ছাড়াই কেবলমাত্র এক ডজন নির্দেশাবলী: coliru.stacked-Crooked.com/a/3b09d36db85ce793
অরল্প

2
@ অ্যালেক্সারিংকিং সম্পন্ন do_moveএখন অত্যন্ত দ্রুত।
অরপাল

16

পাইথন 3 + পাইপাই, 82 80 টি অক্ষর

SWWNNSENESESWSSWSEENWNWSWSEWNWNENENWWSESSEWSWNWSENWEENWWNNESENESSWNWSESESWWNNESE

আমি এই উত্তরটি পোস্ট করতে দ্বিধা বোধ করছি কারণ আমি মূলত orlp এর পদ্ধতিকে গ্রহণ করেছি এবং এতে আমার নিজস্ব স্পিন রেখেছি। এই স্ট্রিংটি সিউডোরোডম দৈর্ঘ্যের 500 টি সমাধান দিয়ে শুরু করে পাওয়া গিয়েছিল - আমি বর্তমান রেকর্ডটি ভেঙে ফেলার আগে বেশ কয়েকটি বীজের চেষ্টা করা হয়েছিল।

একমাত্র নতুন বড় অপটিমাইজেশন হ'ল আমি কেবল মেজদের এক তৃতীয়াংশকে দেখি। দুটি বিভাগের ম্যাজগুলি অনুসন্ধান থেকে বাদ দেওয়া হয়েছে:

  • স্কোয়াডগুলি যেখানে <= 7অ্যাক্সেসযোগ্য Maz
  • মাজেস যেখানে সমস্ত পৌঁছনীয় স্কোয়ারগুলি একক পথে রয়েছে এবং শুরু / সমাপ্তি উভয় প্রান্তে নয়

ধারণাটি হ'ল যে কোনও স্ট্রিং যা বাকি ম্যাজগুলি সমাধান করে তা উপরোক্তভাবে স্বয়ংক্রিয়ভাবে সমাধান করা উচিত। আমি নিশ্চিত যে এটি দ্বিতীয় ধরণের ক্ষেত্রে সত্য, তবে এটি অবশ্যই প্রথমটির ক্ষেত্রে সত্য নয়, সুতরাং আউটপুটে এমন কিছু মিথ্যা ধনাত্মকতা থাকবে যা পৃথকভাবে পরীক্ষা করা দরকার। এই মিথ্যা পজিটিভ সাধারণত প্রায় 20 ম্যাজগুলি মিস করে তবে আমি ভেবেছিলাম এটি গতি এবং যথার্থতার মধ্যে একটি ভাল বাণিজ্য হতে পারে এবং এটি স্ট্রিংগুলিকে পরিবর্তনের জন্য আরও কিছুটা শ্বাস প্রশ্বাসের জায়গা দেয়।

প্রাথমিকভাবে আমি অনুসন্ধানের হিউরিস্টিক্সের দীর্ঘ তালিকার মধ্য দিয়ে গিয়েছিলাম, তবে ভয়াবহভাবে এগুলির মধ্যে কেউই ১৪০ বা তার চেয়ে ভাল কিছু নিয়ে আসতে পারেনি।

import random

N, M = 3, 3

W = 2*N-1
H = 2*M-1

random.seed(142857)


def move(c, cell, walls):
    global W, H

    if c == "N":
        if cell > W and not (1<<(cell-W)//2 & walls):
            cell = cell - W*2

    elif c == "S":
        if cell < W*(H-1) and not (1<<(cell+W)//2 & walls):
            cell = cell + W*2

    elif c == "E":
        if cell % W < W-1 and not (1<<(cell+1)//2 & walls):
            cell = cell + 2

    elif c == "W":
        if cell % W > 0 and not (1<<(cell-1)//2 & walls):
            cell = cell - 2

    return cell


def valid_maze(start, finish, walls):
    global adjacent

    if start == finish:
        return False

    visited = set()
    cells = [start]

    while cells:
        curr_cell = cells.pop()

        if curr_cell == finish:
            return True

        if curr_cell in visited:
            continue

        visited.add(curr_cell)

        for c in "NSEW":
            cells.append(move(c, curr_cell, walls))

    return False


def print_maze(maze):
    start, finish, walls = maze
    print_str = "".join(" #"[walls & (1 << i//2) != 0] if i%2 == 1
                        else " SF"[2*(i==finish) + (i==start)]
                        for i in range(W*H))

    print("#"*(H+2))

    for i in range(H):
        print("#" + print_str[i*W:(i+1)*W] + "#")

    print("#"*(H+2), end="\n\n")

all_cells = [W*y+x for y in range(0, H, 2) for x in range(0, W, 2)]
mazes = []

for start in all_cells:
    for finish in all_cells:
        for walls in range(1<<(N*(M-1) + M*(N-1))):
            if valid_maze(start, finish, walls):
                mazes.append((start, finish, walls))

num_mazes = len(mazes)
print(num_mazes, "mazes generated")

to_remove = set()

for i, maze in enumerate(mazes):
    start, finish, walls = maze

    reachable = set()
    cells = [start]

    while cells:
        cell = cells.pop()

        if cell in reachable:
            continue

        reachable.add(cell)

        if cell == finish:
            continue

        for c in "NSEW":
            new_cell = move(c, cell, walls)
            cells.append(new_cell)

    max_deg = 0
    sf = set()

    for cell in reachable:
        deg = 0

        for c in "NSEW":
            if move(c, cell, walls) != cell:
                deg += 1

        max_deg = max(deg, max_deg)

        if deg == 1:
            sf.add(cell)

    if max_deg <= 2 and len(sf) == 2 and sf != {start, finish}:
        # Single path subset
        to_remove.add(i)

    elif len(reachable) <= (N*M*4)//5:
        # Low reachability maze, above ratio is adjustable
        to_remove.add(i)

mazes = [maze for i,maze in enumerate(mazes) if i not in to_remove]
print(num_mazes - len(mazes), "mazes removed,", len(mazes), "remaining")
num_mazes = len(mazes)


def check(string, cache = set()):
    global mazes

    if string in cache:
        return True

    for i, maze in enumerate(mazes):
        start, finish, walls = maze
        cell = start

        for c in string:
            cell = move(c, cell, walls)

            if cell == finish:
                break

        else:
            # Swap maze to front
            mazes[i//2], mazes[i] = mazes[i], mazes[i//2]
            return False

    cache.add(string)
    return True


while True:
    string = "".join(random.choice("NSEW") for _ in range(500))

    if check(string):
        break

# string = "NWWSSESNESESNNWNNSWNWSSENESWSWNENENWNWESESENNESWSESWNWSWNNEWSESWSEEWNENWWSSNNEESS"

best = len(string)
seen = set()

while True:
    action = random.random()

    if action < 0.1:
        # Grow
        num_grow = int(random.expovariate(lambd=3)) + 1
        new_string = string

        for _ in range(num_grow):
            i = random.randrange(len(new_string))
            new_string = new_string[:i] + random.choice("NSEW") + new_string[i:]

    elif action < 0.2:
        # Swap
        num_swap = int(random.expovariate(lambd=1)) + 1
        new_string = string

        for _ in range(num_swap):
            i,j = sorted(random.sample(range(len(new_string)), 2))
            new_string = new_string[:i] + new_string[j] + new_string[i+1:j] + new_string[i] + new_string[j+1:]

    elif action < 0.35:
        # Mutate
        num_mutate = int(random.expovariate(lambd=1)) + 1
        new_string = string

        for _ in range(num_mutate):
            i = random.randrange(len(new_string))
            new_string = new_string[:i] + random.choice("NSEW") + new_string[i+1:]

    else:
        # Shrink
        num_shrink = int(random.expovariate(lambd=3)) + 1
        new_string = string

        for _ in range(num_shrink):
            i = random.randrange(len(new_string))
            new_string = new_string[:i] + new_string[i+1:]


    if check(new_string):
        string = new_string

    if len(string) <= best and string not in seen:
        while True:
            if len(string) < best:
                seen = set()

            seen.add(string)
            best = len(string)
            print(string, len(string))

            # Force removals on new record strings
            for i in range(len(string)):
                new_string = string[:i] + string[i+1:]

                if check(new_string):
                    string = new_string
                    break

            else:
                break

বৈধ নিশ্চিত করা হয়েছে। দুর্দান্ত উন্নতি :)
ট্রিকোপলাক্স

আমি কিছু ম্যাজগুলি অনুধাবন করার আপনার ধারণাটি যাচাই করার দরকার নেই like কোন মেজগুলি নিরর্থক চেকগুলি নির্ধারণের প্রক্রিয়াটি আপনি কোনওভাবে স্বয়ংক্রিয় করতে পারেন? আমি জানতে আগ্রহী যে এটি স্বতঃস্ফূর্তভাবে অনুমান করা যায়
এমনগুলির

শুরু যেখানে এক প্রান্তে নেই সেখানে পাথ গ্রাফগুলি পরীক্ষা করার প্রয়োজন নেই বলে আপনার যুক্তি কী? যেদিকে ফিনিসটি এক প্রান্তে নেই তার ন্যায্যতা প্রমাণ করা সহজ, এবং ফিনিসটি কাটা শীর্ষে অবস্থিত এমন কেসগুলি পরীক্ষা করার প্রয়োজন না হওয়ার বিষয়ে দৃ strengthened়তর করা যেতে পারে তবে কীভাবে প্রারম্ভিক শীর্ষগুলি বর্জনীয়কে ন্যায়সঙ্গত করা যায় তা আমি দেখতে পাচ্ছি না।
পিটার টেলর

@ পিটারটেলর আরও কিছু চিন্তাভাবনা করার পরে, তাত্ত্বিকভাবে আপনি ঠিক বলেছেন, এমন কিছু ম্যাজ রয়েছে যা আপনি এর মতো মুছে ফেলতে পারবেন না। যাইহোক, এটি দেখে মনে হয় যে 3x3 এ এটি দীর্ঘকালীন স্ট্রিংয়ের জন্য গুরুত্বপূর্ণ নয়।
orlp

2
@orlp, Sp3000 আড্ডায় একটি প্রমাণ স্কেচ করেছে। পাথ গ্রাফগুলি একটি বিশেষ কেস। কোষ পুনরায় নম্বর 0থেকে nপথ বরাবর এবং যে অনুমান করা স্ট্রিং Sথেকে পায় 0করার n। তারপরে Sআপনাকে কোনও মধ্যবর্তী কক্ষ থেকেও পেয়ে cযায় n। মনে করুন অন্যথায়। দিন a(i)পর অবস্থান হতে iশুরু পদক্ষেপ 0এবং b(i)এ শুরু c। তারপর a(0) = 0 < b(0), প্রতিটি পদক্ষেপ পরিবর্তন aএবং bসর্বাধিক 1 দ্বারা, এবং a(|S|) = n > b(|S|)। ক্ষুদ্রতম নিন tযেমন যে a(t) >= b(t)। স্পষ্টতই a(t) != b(t)বা তারা সিঙ্কে থাকবে তাই তাদের অবশ্যই tএকই দিকে অগ্রসর হয়ে ধাপে স্থানগুলি অদলবদল করতে হবে ।
পিটার টেলর

3

অন্তর্বাস থেকে সি ++ এবং লাইব্রেরি

সংক্ষিপ্তসার: একটি নতুন পদ্ধতির, কোনও নতুন সমাধান নয় , খেলতে একটি দুর্দান্ত প্রোগ্রাম এবং পরিচিত সমাধানগুলির স্থানীয় অ-অসম্ভবতার কিছু আকর্ষণীয় ফলাফল। ওহ, এবং কিছু সাধারণভাবে দরকারী পর্যবেক্ষণ।

একটি স্যাট ভিত্তিক পদ্ধতি ব্যবহার করে, আমি সম্পূর্ণরূপে পাতলা দেয়াল এবং বিপরীত কোণগুলিতে স্থির শুরু এবং প্রস্থান অবস্থানের পরিবর্তে অবরুদ্ধ কোষগুলির সাথে 4x4 ম্যাজগুলির জন্য একই ধরণের সমস্যাটি সমাধান করতে পারি । তাই আমি আশা করি এই সমস্যার জন্য একই ধারণা ব্যবহার করতে সক্ষম হবেন। তবে, অন্য সমস্যার জন্য আমি কেবল 2423 ম্যাজ ব্যবহার করেছি (ইতিমধ্যে এটি দেখা গেছে 2083 যথেষ্ট) এবং এটির দৈর্ঘ্যের 29 টি সমাধান রয়েছে, স্যাট এনকোডিং কয়েক মিলিয়ন ভেরিয়েবল ব্যবহার করেছে এবং সমাধানের জন্য কয়েক দিন লেগেছিল।

সুতরাং আমি সিদ্ধান্তটি দুটি গুরুত্বপূর্ণ উপায়ে পরিবর্তন করার সিদ্ধান্ত নিয়েছি:

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

আমি কম ভেরিয়েবল এবং ইউনিট ক্লজগুলি ব্যবহার করতে কিছু অপ্টিমাইজেশনও করেছি।

প্রোগ্রামটি @ orlp এর উপর ভিত্তি করে। একটি গুরুত্বপূর্ণ পরিবর্তন ছিল ম্যাজস নির্বাচন:

  • প্রথমত, ম্যাজগুলি তাদের প্রাচীরের কাঠামো এবং কেবল শুরুর অবস্থান দ্বারা দেওয়া হয়। (তারা পৌঁছনীয় পজিশনগুলিও সঞ্চয় করে)) ফাংশনটি is_solutionপরীক্ষা করে যাচাই করে যে সমস্ত অ্যাক্সেসযোগ্য পজিশনে পৌঁছেছে।
  • (অপরিবর্তিত: এখনও কেবল 4 বা তার থেকে কম পৌঁছনীয় পজিশনযুক্ত ম্যাসেজ ব্যবহার করছে না But তবে তাদের বেশিরভাগ নীচের পর্যবেক্ষণ দ্বারা যে কোনও উপায়ে ফেলে দেওয়া হবে))
  • যদি কোনও গোলকধাঁধা তিনটি শীর্ষ কক্ষের কোনও ব্যবহার না করে, তবে এটি স্থানান্তরিত করা গোলকধাঁধার সমতুল্য। সুতরাং আমরা এটি ড্রপ করতে পারেন। একইভাবে এমন একটি গোলকধাঁধার জন্য যা তিনটি বাম কোষের কোনওটিই ব্যবহার করে না।
  • অ্যাক্সেসযোগ্য অংশগুলি সংযুক্ত রয়েছে কিনা তাতে কিছু যায় আসে না, তাই আমরা দৃ ins়ভাবে জোর দিয়ে বলছি যে প্রতিটি অপ্রাপ্য কোষটি প্রাচীর দ্বারা সম্পূর্ণরূপে ঘিরে রয়েছে।
  • বড় একক পাথ ধাঁধাঁর সাবমাইজ হওয়া একটি একক পাথ ধাঁধাটি সর্বদা সমাধান করা হয় যখন বড়টি সমাধান করা হয়, সুতরাং আমাদের এটির দরকার নেই। সর্বাধিক size আকারের প্রতিটি একক পাথ ধাঁধাঁটি একটি বৃহত্তর অংশের অংশ (এখনও 3x3-এ ফিট করে) তবে সেখানে 8 টি একক পাথ ম্যাসেজ নেই that সরলকিয়ার জন্য, আসুন মাত্র 8 এর চেয়ে কম সাইজের একক পাথ ম্যাসেজগুলি ফেলে দিন ( প্রোগ্রামের।)

এইভাবে, আমি প্রারম্ভিক অবস্থানগুলি সহ মোট 10772 মেজ পাই।

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

#include <algorithm>
#include <array>
#include <bitset>
#include <cstring>
#include <iostream>
#include <set>
#include <vector>
#include <limits>
#include <cassert>

extern "C"{
#include "lglib.h"
}

// reusing a lot of @orlp's ideas and code

enum { N = -8, W = -2, E = 2, S = 8 };
static const int encoded_pos[] = {8, 10, 12, 16, 18, 20, 24, 26, 28};
static const int wall_idx[] = {9, 11, 12, 14, 16, 17, 19, 20, 22, 24, 25, 27};
static const int move_offsets[] = { N, E, S, W };
static const uint32_t toppos = 1ull << 8 | 1ull << 10 | 1ull << 12;
static const uint32_t leftpos = 1ull << 8 | 1ull << 16 | 1ull << 24;
static const int unencoded_pos[] = {0,0,0,0,0,0,0,0,0,0,1,0,2,0,0,0,3,
                                    0,4,0,5,0,0,0,6,0,7,0,8};

int do_move(uint32_t walls, int pos, int move) {
  int idx = pos + move / 2;
  return walls & (1ull << idx) ? pos + move : pos;
}

struct Maze {
  uint32_t walls, reach;
  int start;

  Maze(uint32_t walls=0, uint32_t reach=0, int start=0):
    walls(walls),reach(reach),start(start) {}

  bool is_dummy() const {
    return (walls==0);
  }

  std::size_t size() const{
    return std::bitset<32>(reach).count();
  }

  std::size_t simplicity() const{  // how many potential walls aren't there?
    return std::bitset<32>(walls).count();
  }

};

bool cmp(const Maze& a, const Maze& b){
  auto asz = a.size();
  auto bsz = b.size();
  if (asz>bsz) return true;
  if (asz<bsz) return false;
  return a.simplicity()<b.simplicity();
}

uint32_t reachable(uint32_t walls) {
  static int fill[9];
  uint32_t reached = 0;
  uint32_t reached_relevant = 0;
  for (int start : encoded_pos){
    if ((1ull << start) & reached) continue;
    uint32_t reached_component = (1ull << start);
    fill[0]=start;
    int count=1;
    for(int i=0; i<count; ++i)
      for(int m : move_offsets) {
        int newpos = do_move(walls, fill[i], m);
        if (reached_component & (1ull << newpos)) continue;
        reached_component |= 1ull << newpos;
        fill[count++] = newpos;
      }
    if (count>1){
      if (reached_relevant)
        return 0;  // more than one nonsingular component
      if (!(reached_component & toppos) || !(reached_component & leftpos))
        return 0;  // equivalent to shifted version
      if (std::bitset<32>(reached_component).count() <= 4)
        return 0;  
      reached_relevant = reached_component;
    }
    reached |= reached_component;
  }
  return reached_relevant;
}

void enterMazes(uint32_t walls, uint32_t reached, std::vector<Maze>& mazes){
  int max_deg = 0;
  uint32_t ends = 0;
  for (int pos : encoded_pos)
    if (reached & (1ull << pos)) {
      int deg = 0;
      for (int m : move_offsets) {
        if (pos != do_move(walls, pos, m))
          ++deg;
      }
      if (deg == 1)
        ends |= 1ull << pos;
      max_deg = std::max(deg, max_deg);
    }
  uint32_t starts = reached;
  if (max_deg == 2){
    if (std::bitset<32>(reached).count() <= 7)
      return; // small paths are redundant
    starts = ends; // need only start at extremal points
  }
  for (int pos : encoded_pos)
    if ( starts & (1ull << pos))
      mazes.emplace_back(walls, reached, pos);
}

std::vector<Maze> gen_valid_mazes() {
  std::vector<Maze> mazes;
  for (int maze_id = 0; maze_id < (1 << 12); maze_id++) {
    uint32_t walls = 0;
    for (int i = 0; i < 12; ++i) 
      if (maze_id & (1 << i))
    walls |= 1ull << wall_idx[i];
    uint32_t reached=reachable(walls);
    if (!reached) continue;
    enterMazes(walls, reached, mazes);
  }
  std::sort(mazes.begin(),mazes.end(),cmp);
  return mazes;
};

bool is_solution(const std::vector<int>& moves, Maze& maze) {
  int pos = maze.start;
  uint32_t reached = 1ull << pos;
  for (auto move : moves) {
    pos = do_move(maze.walls, pos, move);
    reached |= 1ull << pos;
    if (reached == maze.reach) return true;
  }
  return false;
}

std::vector<int> str_to_moves(std::string str) {
  std::vector<int> moves;
  for (auto c : str) {
    switch (c) {
    case 'N': moves.push_back(N); break;
    case 'E': moves.push_back(E); break;
    case 'S': moves.push_back(S); break;
    case 'W': moves.push_back(W); break;
    }
  }
  return moves;
}

Maze unsolved(const std::vector<int>& moves, std::vector<Maze>& mazes) {
  int unsolved_count = 0;
  Maze problem{};
  for (Maze m : mazes)
    if (!is_solution(moves, m))
      if(!(unsolved_count++))
    problem=m;
  if (unsolved_count)
    std::cout << "unsolved: " << unsolved_count << "\n";
  return problem;
}

LGL * lgl;

constexpr int TRUELIT = std::numeric_limits<int>::max();
constexpr int FALSELIT = -TRUELIT;

int new_var(){
  static int next_var = 1;
  assert(next_var<TRUELIT);
  return next_var++;
}

bool lit_is_true(int lit){
  int abslit = lit>0 ? lit : -lit;
  bool res = (abslit==TRUELIT) || (lglderef(lgl,abslit)>0);
  return lit>0 ? res : !res;
}

void unsat(){
  std::cout << "Unsatisfiable!\n";
  std::exit(1);
}

void clause(const std::set<int>& lits){
  if (lits.find(TRUELIT) != lits.end())
    return;
  for (int lit : lits)
    if (lits.find(-lit) != lits.end())
      return;
  int found=0;
  for (int lit : lits)
    if (lit != FALSELIT){
      lgladd(lgl, lit);
      found=1;
    }
  lgladd(lgl, 0);
  if (!found)
    unsat();
}

void at_most_one(const std::set<int>& lits){
  if (lits.size()<2)
    return;
  for(auto it1=lits.cbegin(); it1!=lits.cend(); ++it1){
    auto it2=it1;
    ++it2;
    for(  ; it2!=lits.cend(); ++it2)
      clause( {- *it1, - *it2} );
  }
}

/* Usually, lit_op(lits,sgn) creates a new variable which it returns,
   and adds clauses that ensure that the variable is equivalent to the
   disjunction (if sgn==1) or the conjunction (if sgn==-1) of the literals
   in lits. However, if this disjunction or conjunction is constant True
   or False or simplifies to a single literal, that is returned without
   creating a new variable and without adding clauses.                    */ 

int lit_op(std::set<int> lits, int sgn){
  if (lits.find(sgn*TRUELIT) != lits.end())
    return sgn*TRUELIT;
  lits.erase(sgn*FALSELIT);
  if (!lits.size())
    return sgn*FALSELIT;
  if (lits.size()==1)
    return *lits.begin();
  int res=new_var();
  for(int lit : lits)
    clause({sgn*res,-sgn*lit});
  for(int lit : lits)
    lgladd(lgl,sgn*lit);
  lgladd(lgl,-sgn*res);
  lgladd(lgl,0);
  return res;
}

int lit_or(std::set<int> lits){
  return lit_op(lits,1);
}

int lit_and(std::set<int> lits){
  return lit_op(lits,-1);
}

using A4 = std::array<int,4>;

void add_maze_conditions(Maze m, std::vector<A4> dirs, int len){
  int mp[9][2];
  int rp[9];
  for(int p=0; p<9; ++p)
    if((1ull << encoded_pos[p]) & m.reach)
      rp[p] = mp[p][0] = encoded_pos[p]==m.start ? TRUELIT : FALSELIT;
  int t=0;
  for(int i=0; i<len; ++i){
    std::set<int> posn {};
    for(int p=0; p<9; ++p){
      int ep = encoded_pos[p];
      if((1ull << ep) & m.reach){
        std::set<int> reach_pos {};
        for(int d=0; d<4; ++d){
          int np = do_move(m.walls, ep, move_offsets[d]);
          reach_pos.insert( lit_and({mp[unencoded_pos[np]][t],
                                  dirs[i][d ^ ((np==ep)?0:2)]    }));
        }
        int pl = lit_or(reach_pos);
        mp[p][!t] = pl;
        rp[p] = lit_or({rp[p], pl});
        posn.insert(pl);
      }
    }
    at_most_one(posn);
    t=!t;
  }
  for(int p=0; p<9; ++p)
    if((1ull << encoded_pos[p]) & m.reach)
      clause({rp[p]});
}

void usage(char* argv0){
  std::cout << "usage: " << argv0 <<
    " <string>\n   where <string> consists of 'N', 'E', 'S', 'W' and '*'.\n" ;
  std::exit(2);
}

const std::string nesw{"NESW"};

int main(int argc, char** argv) {
  if (argc!=2)
    usage(argv[0]);
  std::vector<Maze> mazes = gen_valid_mazes();
  std::cout << "Mazes with start positions: " << mazes.size() << "\n" ;
  lgl = lglinit();
  int len = std::strlen(argv[1]);
  std::cout << argv[1] << "\n   with length " << len << "\n";

  std::vector<A4> dirs;
  for(int i=0; i<len; ++i){
    switch(argv[1][i]){
    case 'N':
      dirs.emplace_back(A4{TRUELIT,FALSELIT,FALSELIT,FALSELIT});
      break;
    case 'E':
      dirs.emplace_back(A4{FALSELIT,TRUELIT,FALSELIT,FALSELIT});
      break;
    case 'S':
      dirs.emplace_back(A4{FALSELIT,FALSELIT,TRUELIT,FALSELIT});
      break;
    case 'W':
      dirs.emplace_back(A4{FALSELIT,FALSELIT,FALSELIT,TRUELIT});
      break;
    case '*': {
      dirs.emplace_back();
      std::generate_n(dirs[i].begin(),4,new_var);
      std::set<int> dirs_here { dirs[i].begin(), dirs[i].end() };
      at_most_one(dirs_here);
      clause(dirs_here);
      for(int l : dirs_here)
        lglfreeze(lgl,l);
      break;
      }
    default:
      usage(argv[0]);
    }
  }

  int maze_nr=0;
  for(;;) {
    std::cout << "Solving...\n";
    int res=lglsat(lgl);
    if(res==LGL_UNSATISFIABLE)
      unsat();
    assert(res==LGL_SATISFIABLE);
    std::string sol(len,' ');
    for(int i=0; i<len; ++i)
      for(int d=0; d<4; ++d)
        if (lit_is_true(dirs[i][d])){
          sol[i]=nesw[d];
          break;
    }
    std::cout << sol << "\n";

    Maze m=unsolved(str_to_moves(sol),mazes);
    if (m.is_dummy()){
      std::cout << "That solves all!\n";
      return 0;
    }
    std::cout << "Adding maze " << ++maze_nr << ": " << 
      m.walls << "/" << m.start <<
      " (" << m.size() << "/" << 12-m.simplicity() << ")\n";
    add_maze_conditions(m,dirs,len);
  }
}  

প্রথম configure.shএবং সমাধানকারী, তারপর কিছু প্রোগ্রাম কম্পাইল মত , যেখানে পথ কোথায় রেস্প। হয়, সুতরাং উভয় উদাহরণস্বরূপ হতে পারে । বা কেবল তাদের একই ডিরেক্টরিতে রেখে দিন এবং বিকল্পগুলি ছাড়াই করুন।makelingelingg++ -std=c++11 -O3 -I ... -o m3sat m3sat.cc -L ... -llgl...lglib.hliblgl.a../lingeling-<version>-I-L

প্রোগ্রাম এক বাধ্যতামূলক কমান্ড লাইন আর্গুমেন্ট লাগে, একটি স্ট্রিং এর মধ্যে রয়েছে N, E, S, W(স্থির দিকনির্দেশের জন্য) অথবা *। আপনি 78 একটি স্ট্রিং দিয়ে আকার 78 একটি সাধারণ সমাধান জন্য অনুসন্ধান সুতরাং পারে *গুলি (উদ্ধৃতির মধ্যে), অথবা একটি সমাধান জন্য অনুসন্ধান দিয়ে শুরু NEWSব্যবহারের NEWSঅনেক হিসাবে দ্বারা অনুসরণ *আপনি অতিরিক্ত ধাপের জন্য চান হিসাবে সে। প্রথম পরীক্ষা হিসাবে, আপনার পছন্দসই সমাধানটি নিন এবং কয়েকটি অক্ষরের সাথে প্রতিস্থাপন করুন *। "কিছু" এর আশ্চর্যজনকভাবে উচ্চ মানের জন্য এটি দ্রুত সমাধান খুঁজে পায়।

প্রোগ্রামটি জানায় যে এটি কোন ধাঁধাঁটি যুক্ত করে, প্রাচীরের কাঠামো এবং সূচনা অবস্থান দ্বারা বর্ণিত, এবং পৌঁছনীয় পজিশন এবং দেয়ালের সংখ্যাও দেবে। ম্যাজগুলি এই মানদণ্ড অনুসারে বাছাই করা হয় এবং প্রথম অমীমাংসিত এক যুক্ত করা হয়। অতএব সর্বাধিক সংযোজন করা মেজেগুলি রয়েছে (9/4)তবে কখনও কখনও অন্যরাও উপস্থিত হন।

আমি length৯ দৈর্ঘ্যের জ্ঞাত সমাধানটি নিয়েছি এবং সংলগ্ন 26 টি অক্ষরের প্রতিটি গ্রুপের জন্য 25 টি অক্ষর দিয়ে সেগুলি প্রতিস্থাপন করার চেষ্টা করেছি। আমি শুরু থেকে এবং শেষ থেকে 13 টি অক্ষর অপসারণ করার চেষ্টা করেছি এবং এগুলিকে শুরুতে যে কোনও 13 এবং শেষে 12 টি দ্বারা প্রতিস্থাপন করেছি এবং বিপরীতে। দুর্ভাগ্যক্রমে, এটি সমস্ত অসন্তুষ্টির বাইরে এসেছিল। সুতরাং, আমরা এটি সূচক হিসাবে নিতে পারি যে দৈর্ঘ্য 79৯টি সর্বোত্তম? না, আমি একইভাবে 80 সমাধানের দৈর্ঘ্য 79 এর দৈর্ঘ্যের উন্নতি করার চেষ্টা করেছি এবং এটিও সফল হয়নি।

অবশেষে, আমি একটি সমাধানের সূচনার সাথে অন্যটির শেষের সাথে এবং একই সাথে প্রতিসাম্যগুলির মধ্যে একটি দ্বারা পরিবর্তিত একটি সমাধানের সাথে একত্রিত করার চেষ্টা করেছি। এখন আমি আকর্ষণীয় ধারণাগুলির বাইরে চলে যাচ্ছি, সুতরাং আমি নতুন সিদ্ধান্তগুলি না নিয়ে সত্ত্বেও আমার কাছে যা আছে তা আপনাকে দেখানোর সিদ্ধান্ত নিয়েছি।


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

পুরানো সম্পর্কিত চ্যালেঞ্জের জন্য সর্বোত্তম দৈর্ঘ্যের সমাধান সন্ধান করার জন্য দুর্দান্ত কাজ !
ট্রাইকোপল্যাক্স
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.