ওয়েট এলোমেলো আইটেম পান


51

আমি উদাহরণস্বরূপ, এই টেবিল আছে

+ + ----------------- + +
| ফল | ওজন |
+ + ----------------- + +
| আপেল | 4 |
| কমলা | 2 |
| লেবু | 1 |
+ + ----------------- + +

আমার এলোমেলো ফল ফিরিয়ে দেওয়া দরকার। কিন্তু আপেল হিসাবে ঘন যেমন 4 বার বাছাই করা হবে লেবু যত ঘন এবং 2 বার কমলা

আরও সাধারণ ক্ষেত্রে এটি f(weight)ঘন ঘন হওয়া উচিত ।

এই আচরণটি বাস্তবায়নের জন্য একটি ভাল সাধারণ অ্যালগরিদম কী?

নাকি রুবিতে কিছু প্রস্তুত রত্ন রয়েছে? :)

PS
আমি রুবি https://github.com/fl00r/pickup এ বর্তমান অ্যালগরিদম প্রয়োগ করেছি


11
ডায়াবলোতে এলোমেলো লুট পাওয়ার জন্য একই সূত্রটি হওয়া উচিত :-)
জালেন

1
@ জালেন: প্রকৃতপক্ষে, আমার উত্তরটিতে অন্তর্বর্তী সমাধানের ধারণাটি ওয়ার্কক্র্যাফট ওয়ার্ল্ডের যুদ্ধের টেবিলগুলির বিষয়ে আমার মনে আছে from :-D
বেনিয়ামিন ক্লোস্টার



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

উত্তর:


50

ধারণাগতভাবে সহজ সমাধান হ'ল একটি তালিকা তৈরি করা যেখানে প্রতিটি উপাদান তার ওজন হিসাবে বহুগুণ ঘটে, তাই

fruits = [apple, apple, apple, apple, orange, orange, lemon]

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


আরেকটি, কিছুটা জটিল পদ্ধতির মতো দেখতে হবে:

  1. ওজনের সংখ্যামূলক যোগফল গণনা করুন:

    intervals = [4, 6, 7]

    যেখানে নীচের 4 এর সূচক একটি আপেল প্রতিনিধিত্ব করে , 4 থেকে নীচে কমলা এবং 6 থেকে নীচে একটি লেবুকে উপস্থাপন করে

  2. একটি র্যান্ডম সংখ্যা তৈরি করুন nসীমার মধ্যে 0থেকে sum(weights)

  3. শেষের আইটেমটি সন্ধান করুন যার সমষ্টিগত যোগফল উপরে n। সম্পর্কিত ফলটি আপনার ফলাফল।

এই পদ্ধতির জন্য প্রথমটির চেয়ে আরও জটিল কোড দরকার, তবে কম মেমরি এবং গণনা এবং ভাসমান-পয়েন্ট ওজনকে সমর্থন করে।

উভয়ই অ্যালগরিদমের জন্য, একবারে নির্বিচারে নির্বাচনের জন্য নির্ধারণ করা যেতে পারে সেটআপ-পদক্ষেপ।


2
অন্তর্বর্তী সমাধানটি দুর্দান্ত বলে মনে হচ্ছে
জালান

1
এটি আমার প্রথম চিন্তা ছিল :)। তবে আমি যদি 100 টি ফল এবং ওজন 10 কেটের কাছাকাছি টেবিল পেয়েছি? এটি খুব বড় অ্যারে হবে এবং এটি আমি চাই যতটা দক্ষ হবে না। এটি প্রথম সমাধান সম্পর্কে। দ্বিতীয় সমাধানটি দেখতে দুর্দান্ত
fl00r

1
আমি এই অ্যালগরিদমটি রুবি github.com/fl00r/pickup-
fl00r

1
এফেস পদ্ধতি হ'ল এটির হ'ল ডিফাক্টো উপায় হ'ল আমি উলামের পদ্ধতি উপেক্ষা করে একই বার বার একই কোডটির পুনরাবৃত্তি করে এমন পোস্টের সংখ্যায় সত্যই অবাক হয়েছি । sakeশ্বরের দোহাই দিয়ে আপনি স্থির সময় পারফরম্যান্স পান!
opa

30

এখানে একটি অ্যালগরিদম রয়েছে (সি # তে) যে কোনও অনুক্রম থেকে এলোমেলোভাবে ওজনযুক্ত উপাদান নির্বাচন করতে পারে, কেবল এটির মাধ্যমে একবারে পুনরাবৃত্তি হবে:

public static T Random<T>(this IEnumerable<T> enumerable, Func<T, int> weightFunc)
{
    int totalWeight = 0; // this stores sum of weights of all elements before current
    T selected = default(T); // currently selected element
    foreach (var data in enumerable)
    {
        int weight = weightFunc(data); // weight of current element
        int r = Random.Next(totalWeight + weight); // random value
        if (r >= totalWeight) // probability of this is weight/(totalWeight+weight)
            selected = data; // it is the probability of discarding last selected element and selecting current one instead
        totalWeight += weight; // increase weight sum
    }

    return selected; // when iterations end, selected is some element of sequence. 
}

এটি নিম্নলিখিত যুক্তির উপর ভিত্তি করে: আসুন "বর্তমান ফলাফল" হিসাবে আমাদের ক্রমের প্রথম উপাদানটি নির্বাচন করুন; তারপরে, প্রতিটি পুনরাবৃত্তির উপর, এটি রাখুন বা বাতিল করুন এবং নতুন উপাদানটিকে বর্তমান হিসাবে বেছে নিন। আমরা শেষ পর্যায়ে যে কোনও উপাদানকে নির্বাচিত হওয়ার সম্ভাবনাগুলি গণনা করতে পারি যে এটি পরবর্তী ধাপগুলিতে বাদ দেওয়া হবে না , সম্ভাব্যতার পরে এটি প্রথম স্থানে নির্বাচিত হবে times আপনি যদি গণিতটি করেন, আপনি দেখতে পাবেন যে এই পণ্যটি (উপাদানটির ওজন) / (সমস্ত ওজনের যোগফল) এর জন্য সরল করে তোলে, যা আমাদের প্রয়োজন ঠিক একই রকম!

যেহেতু এই পদ্ধতিটি কেবল একবার ইনপুট অনুক্রমের উপরে পুনরাবৃত্তি করে, এটি অশ্লীলভাবে বড় আকারের ক্রমগুলির সাথেও কাজ করে, তবে শর্ত থাকে যে ওজনের যোগফলগুলি একটি মাপসই হয় int(বা আপনি এই কাউন্টারটির জন্য কিছু বড় ধরণের চয়ন করতে পারেন)


2
আমি একবারে এটির পুনরাবৃত্তি হওয়ায় এটি আরও ভাল বলে ধরে নেওয়ার আগে আমি এটি বেঞ্চমার্ক করব। অনেকগুলি এলোমেলো মান উত্পন্ন করা ঠিক তত দ্রুত নয়।
জিন-বার্নার্ড পেলারিন

1
@ জিন-বার্নার্ড পেলেরিন আমি করেছি এবং বড় তালিকাতে এটি আসলে দ্রুত। আপনি ক্রিপ্টোগ্রাফিকভাবে শক্তিশালী এলোমেলো জেনারেটর ব্যবহার না করে (-8
কখনই নয়

ইমো গ্রহণযোগ্য উত্তর হওয়া উচিত। আমি এটি "বিরতি" এবং "পুনরাবৃত্তি প্রবেশ" পদ্ধতির চেয়ে ভাল better
ভিভিন পালিথ

2
আমি কেবল বলতে চেয়েছিলাম যে আমি এই পদ্ধতিটি ব্যবহার করতে গত কয়েক বছরে 3 বা 4 বার এই থ্রেডে ফিরে এসেছি। এই উদ্দেশ্যে আমার উদ্দেশ্যগুলির জন্য আমার দ্রুত প্রয়োজনীয় উত্তরগুলি সরবরাহ করতে বারবার সাফল্য পেয়েছে। আমি আশা করি যখনই আমি এই উত্তরটি ব্যবহার করতে ফিরে এসেছি তখন এই উত্তরটি উঁচু করে তুলতে পারি।
জিম ইয়ারব্রো

1
আপনার যদি সত্যিই কেবল একবার চয়ন করতে হয় তবে দুর্দান্ত সমাধান। অন্যথায়, প্রথম উত্তরে একবার সমাধানের জন্য প্রস্তুতিমূলক কাজ করা আরও কার্যকর।
20-10

22

ইতিমধ্যে উপস্থিত উত্তরগুলি ভাল এবং আমি তাদের উপর কিছুটা প্রসারিত করব।

যেমন বেনিয়ামিন পরামর্শ দিয়েছিলেন যে ক্রমবহুল পরিমাণগুলি সাধারণত এই ধরণের সমস্যায় ব্যবহৃত হয়:

+------------------------+
| fruit  | weight | csum |
+------------------------+
| apple  |   4    |   4  |
| orange |   2    |   6  |
| lemon  |   1    |   7  |
+------------------------+

এই কাঠামোর কোনও আইটেম সন্ধান করতে আপনি কখনই নমনমাইন্ডের কোড টুকরো জাতীয় কিছু ব্যবহার করতে পারেন। এই # সি কোডের টুকরা যা আমি সাধারণত ব্যবহার করি:

double r = Random.Next() * totalSum;
for(int i = 0; i < fruit.Count; i++)
{
    if (csum[i] > r)
        return fruit[i];
}

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

double r = Random.Next() * totalSum;
int lowGuess = 0;
int highGuess = fruit.Count - 1;

while (highGuess >= lowGuess)
{
    int guess = (lowGuess + highGuess) / 2;
    if ( csum[guess] < r)
        lowGuess = guess + 1;
    else if ( csum[guess] - weight[guess] > r)
        highGuess = guess - 1;
    else
        return fruit[guess];
}

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


বাইনারি অনুসন্ধান সম্পর্কে ভাল কথা
fl00r

মাইন্ডমাইন্ডের উত্তরে অতিরিক্ত স্থানের প্রয়োজন নেই, সুতরাং এটি ও (1) নয়, তবে বারবার এলোমেলো সংখ্যা তৈরি করে ওজন ফাংশন (যা অন্তর্নিহিত সমস্যার উপর নির্ভর করে ব্যয়বহুল হতে পারে) মূল্যায়ন করে রানটাইম জটিলতা যুক্ত করে।
বেনিয়ামিন ক্লোস্টার

1
আপনি আমার কোডটির "আরও পঠনযোগ্য সংস্করণ" বলে দাবি করেন যা আসলে তা নয়। আপনার কোডটিতে মোট ওজনের যোগফল এবং মোট পরিমাণগুলি আগেভাগে জানতে হবে; আমার না।
29:55

@ বেনজামিন ক্লোস্টার আমার কোড কেবলমাত্র উপাদান অনুসারে একবার ওজন ফাংশন কল করে - আপনি এর চেয়ে ভাল আর কিছু করতে পারবেন না। যদিও আপনি এলোমেলো সংখ্যার কথা ঠিক বলেছেন।
কিছু মনে রাখবেন না

@ নেভারমাইন্ড: আপনি কেবল পিক-ফাংশনে একবার কল করেছেন, সুতরাং ব্যবহারকারী যদি এটিতে দুবার কল করেন, প্রতিটি উপাদানটির জন্য আবার ওজন ফাংশন কল করা হয়। অবশ্যই আপনি এটি ক্যাশে করতে পারেন, তবে তারপরেও আপনি আর স্থানের জটিলতার জন্য ও (1) নন।
বেনজামিন ক্লোস্টার

8

এটি একটি সাধারণ পাইথন বাস্তবায়ন:

from random import random

def select(container, weights):
    total_weight = float(sum(weights))
    rel_weight = [w / total_weight for w in weights]

    # Probability for each element
    probs = [sum(rel_weight[:i + 1]) for i in range(len(rel_weight))]

    slot = random()
    for (i, element) in enumerate(container):
        if slot <= probs[i]:
            break

    return element

এবং

population = ['apple','orange','lemon']
weights = [4, 2, 1]

print select(population, weights)

জেনেটিক অ্যালগরিদমে এই নির্বাচন পদ্ধতিটিকে ফিটনেস অনুপাতমূলক নির্বাচন বা রোলিট হুইল নির্বাচন বলা হয় :

  • চক্রের একটি অনুপাত তাদের ওজন মানের উপর ভিত্তি করে প্রতিটি সম্ভাব্য নির্বাচনের জন্য বরাদ্দ করা হয়। সমস্ত নির্বাচনের মোট ওজন দ্বারা নির্বাচনের ওজন ভাগ করে এটি সাধারনত 1 এ অর্জন করা যেতে পারে।
  • তারপরে র্যান্ডলেটের চাকাটি কীভাবে ঘোরানো হয় তার মতো একটি এলোমেলো নির্বাচন করা হয়।

রুলেট চাকা নির্বাচন

সাধারণ অ্যালগরিদমে O (N) বা O (লগ এন) জটিলতা থাকে তবে আপনি ও (1 )ও করতে পারেন (যেমন স্টোকাস্টিক স্বীকৃতির মাধ্যমে রুলেট-হুইল নির্বাচন )।


আপনি কি জানেন যে এই চিত্রটির মূল উত্সটি কী? আমি এটি একটি কাগজের জন্য ব্যবহার করতে চাই তবে এট্রিবিউশনের বিষয়টি নিশ্চিত করা দরকার।
ম্যালকম ম্যাকলিয়ড

@ মালকলমম্যাকলিউড দুঃখিত, এটি প্রচুর জিএ পেপারস / সাইটগুলিতে ব্যবহৃত হয়েছে তবে লেখক কে তা আমি জানি না।
ম্যানলিও

0

এই सारটি আপনি যা চাইছেন ঠিক তাই করছে।

public static Random random = new Random(DateTime.Now.Millisecond);
public int chooseWithChance(params int[] args)
    {
        /*
         * This method takes number of chances and randomly chooses
         * one of them considering their chance to be choosen.    
         * e.g. 
         *   chooseWithChance(0,99) will most probably (%99) return 1
         *   chooseWithChance(99,1) will most probably (%99) return 0
         *   chooseWithChance(0,100) will always return 1.
         *   chooseWithChance(100,0) will always return 0.
         *   chooseWithChance(67,0) will always return 0.
         */
        int argCount = args.Length;
        int sumOfChances = 0;

        for (int i = 0; i < argCount; i++) {
            sumOfChances += args[i];
        }

        double randomDouble = random.NextDouble() * sumOfChances;

        while (sumOfChances > randomDouble)
        {
            sumOfChances -= args[argCount -1];
            argCount--;
        }

        return argCount-1;
    }

আপনি এটির মতো ব্যবহার করতে পারেন:

string[] fruits = new string[] { "apple", "orange", "lemon" };
int choosenOne = chooseWithChance(98,1,1);
Console.WriteLine(fruits[choosenOne]);

উপরের কোডটি সম্ভবত (% 98) 0 প্রদান করবে যা প্রদত্ত অ্যারের জন্য 'আপেল' এর সূচক।

এছাড়াও, এই কোডটি উপরে প্রদত্ত পদ্ধতিটি পরীক্ষা করে:

Console.WriteLine("Start...");
int flipCount = 100;
int headCount = 0;
int tailsCount = 0;

for (int i=0; i< flipCount; i++) {
    if (chooseWithChance(50,50) == 0)
        headCount++;
    else
        tailsCount++;
}

Console.WriteLine("Head count:"+ headCount);
Console.WriteLine("Tails count:"+ tailsCount);

এটি এরূপ একটি আউটপুট দেয়:

Start...
Head count:52
Tails count:48

2
প্রোগ্রামাররা হয় সম্পর্কে ধারণাগত প্রশ্ন ও উত্তর জিনিষ ব্যাখ্যা করার আশা করা যায়। ব্যাখ্যার পরিবর্তে কোড ডাম্প নিক্ষেপ করা আইডিই থেকে হোয়াইটবোর্ডে কোড অনুলিপি করার মতো: এটি পরিচিত এবং এমনকি কখনও কখনও বোধগম্য হতে পারে তবে এটি অদ্ভুত বোধ করে ... ঠিক অদ্ভুত। হোয়াইটবোর্ড কম্পাইলার নেই
মশা

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