কীভাবে একটি ওয়েট শ্যাফেল কার্যকর করা যায়


22

আমি সম্প্রতি কিছু কোড লিখেছি যা আমি খুব অদক্ষ মনে করেছিলাম, তবে যেহেতু এটিতে কয়েকটি কয়েকটি মান অন্তর্ভুক্ত ছিল তাই আমি এটি গ্রহণ করেছি। তবে, আমি এখনও নিম্নলিখিতগুলির জন্য আরও ভাল অ্যালগরিদমে আগ্রহী:

  1. এক্স অবজেক্টের একটি তালিকা, তাদের প্রত্যেককে একটি "ওজন" বরাদ্দ করা হয়েছে
  2. ওজন যোগ করুন
  3. যোগফল থেকে 0 পর্যন্ত একটি এলোমেলো সংখ্যা তৈরি করুন
  4. বস্তুগুলির মাধ্যমে আইট্রেট করুন, যোগফলটি অ-ধনাত্মক না হওয়া পর্যন্ত তাদের ওজনের যোগফল থেকে বিয়োগ করুন
  5. তালিকা থেকে অবজেক্টটি সরান, এবং তারপরে এটিকে নতুন তালিকার শেষে যুক্ত করুন

আইটেম 2,4, এবং 5 সমস্ত nসময় নেয় এবং তাই এটি একটি O(n^2)অ্যালগরিদম।

এই উন্নতি করা যায়?

ওজনযুক্ত পরিবর্তনের উদাহরণ হিসাবে, কোনও উপাদানটির উচ্চতর ওজন নিয়ে সামনের দিকে থাকার আরও বেশি সম্ভাবনা থাকে।

উদাহরণ (আমি এটিকে বাস্তব করতে এলোমেলো সংখ্যা তৈরি করব):

6,5,4,3,2,1 ওজন সহ 6 টি অবজেক্ট; যোগফল 21

আমি 19 বাছাই করেছি: 19-6-5-4-3-2 = -1এভাবে 2 প্রথম অবস্থানে যায়, ওজন এখন 6,5,4,3,1; যোগফল 19

আমি 16 বাছাই করেছি: 16-6-5-4-3 = -2এইভাবে 3 দ্বিতীয় অবস্থানে যায়, ওজন এখন 6,5,4,1; যোগফল 16

আমি 3 বাছাই করেছি: 3-6 = -3সুতরাং 6 তৃতীয় অবস্থানে চলে যায়, ওজন এখন 5,4,1; যোগফল 10

আমি 8 বাছাই করেছি: 8-5-4 = -1এইভাবে 4 চতুর্থ অবস্থানে যায়, ওজন এখন 5,1; যোগফল 6

আমি 5 বাছাই করেছি: 5-5=0এইভাবে 5 পঞ্চম অবস্থানে যায়, ওজন এখন 1 হয়; যোগফল 1

আমি 1 বাছাই করেছি: 1-1=0এভাবে 1 শেষ পজিশনে চলে যায়, আমার আর ওজন নেই, শেষ করছি


6
ওজনযুক্ত পাল্টানো ঠিক কী? এর অর্থ কী ওজন বেশি হবে, ততই ডেকের শীর্ষে বস্তুটি হওয়ার সম্ভাবনা বেশি?
ডোভাল

কৌতূহলের বাইরে, পদক্ষেপের উদ্দেশ্য কী (5)। তালিকাটি স্থির থাকলে এটিকে উন্নত করার উপায় রয়েছে।
রোবট

হ্যাঁ, ডোভাল। আমি তালিকাটি থেকে আইটেমটি সরিয়ে ফেলছি যাতে এটি পরিবর্তন হওয়া তালিকায় একাধিকবার প্রদর্শিত না হয়।
নাথান মেরিল

তালিকার কোনও আইটেমের ওজন কি অবিচ্ছিন্ন?

একটি আইটেমের তুলনায় অন্যটির চেয়ে বেশি ওজন থাকবে তবে আইটেম এক্সের সর্বদা একই ওজন থাকবে। (স্পষ্টতই, আপনি যদি আইটেমগুলি সরিয়ে ফেলেন তবে বৃহত্তর ওজন অনুপাতে আরও বড় হবে)
নাথান মেরিল

উত্তর:


14

এটি O(n log(n))একটি গাছ ব্যবহার করে প্রয়োগ করা যেতে পারে ।

প্রথমে প্রতিটি নোডের ডানদিকে এবং বামে সমস্ত বংশধর নোডের সংমিশ্রণ যোগ রেখে গাছটি তৈরি করুন।

কোনও আইটেম নমুনা করতে, আপনি বর্তমান নোড, বাম থেকে একটি নোড বা ডান থেকে একটি নোড ফিরে আসবেন কিনা তা নির্ধারণের জন্য ক্রমবর্ধমান অঙ্কগুলি ব্যবহার করে রুট নোড থেকে পুনরাবৃত্তভাবে নমুনা করুন। প্রতিবার যখন আপনি কোনও নোডের নমুনা করবেন তখন এর ওজন শূন্যে সেট করুন এবং প্যারেন্ট নোডগুলিও আপডেট করুন।

পাইথনে এটি আমার বাস্তবায়ন:

import random

def weigthed_shuffle(items, weights):
    if len(items) != len(weights):
        raise ValueError("Unequal lengths")

    n = len(items)
    nodes = [None for _ in range(n)]

    def left_index(i):
        return 2 * i + 1

    def right_index(i):
        return 2 * i + 2

    def total_weight(i=0):
        if i >= n:
            return 0
        this_weigth = weights[i]
        if this_weigth <= 0:
            raise ValueError("Weigth can't be zero or negative")
        left_weigth = total_weight(left_index(i))
        right_weigth = total_weight(right_index(i))
        nodes[i] = [this_weigth, left_weigth, right_weigth]
        return this_weigth + left_weigth + right_weigth

    def sample(i=0):
        this_w, left_w, right_w = nodes[i]
        total = this_w + left_w + right_w
        r = total * random.random()
        if r < this_w:
            nodes[i][0] = 0
            return i
        elif r < this_w + left_w:
            chosen = sample(left_index(i))
            nodes[i][1] -= weights[chosen]
            return chosen
        else:
            chosen = sample(right_index(i))
            nodes[i][2] -= weights[chosen]
            return chosen

    total_weight() # build nodes tree

    return (items[sample()] for _ in range(n - 1))

ব্যবহার:

In [2]: items = list(range(10))
   ...: weights = list(range(10, 0, -1))
   ...:

In [3]: for _ in range(10):
   ...:     print(list(weigthed_shuffle(items, weights)))
   ...:
[5, 0, 8, 6, 7, 2, 3, 1, 4]
[1, 2, 5, 7, 3, 6, 9, 0, 4]
[1, 0, 2, 6, 8, 3, 7, 5, 4]
[4, 6, 8, 1, 2, 0, 3, 9, 7]
[3, 5, 1, 0, 4, 7, 2, 6, 8]
[3, 7, 1, 2, 0, 5, 6, 4, 8]
[1, 4, 8, 2, 6, 3, 0, 9, 5]
[3, 5, 0, 4, 2, 6, 1, 8, 9]
[6, 3, 5, 0, 1, 2, 4, 8, 7]
[4, 1, 2, 0, 3, 8, 6, 5, 7]

weigthed_shuffleএকটি জেনারেটর, যাতে আপনি শীর্ষ kআইটেমগুলি দক্ষতার সাথে নমুনা করতে পারেন । আপনি যদি পুরো অ্যারে পরিবর্তন করতে চান তবে ক্লান্তি ( listফাংশনটি ব্যবহার করে ) জেনারেটরের উপরে পুনরাবৃত্তি করুন ।

হালনাগাদ:

ভারী র্যান্ডম স্যাম্পলিং (2005; ইফ্রাইমিডিস, স্পিরাকিস) এর জন্য খুব মার্জিত আলগোরিদিম সরবরাহ করে। বাস্তবায়ন অত্যন্ত সহজ, এবং এছাড়াও সঞ্চালিত O(n log(n)):

def weigthed_shuffle(items, weights):
    order = sorted(range(len(items)), key=lambda i: -random.random() ** (1.0 / weights[i]))
    return [items[i] for i in order]

শেষ আপডেটটি চূড়ান্তভাবে একটি ভুল ওয়ান-লাইনারের সমাধানের মতো বলে মনে হচ্ছে । আপনি কি নিশ্চিত যে এটি সঠিক?
গিয়াকোমো আলজেটা

19

সম্পাদনা: এই উত্তরটি ওজনকে যেভাবে প্রত্যাশিত হবে তা ব্যাখ্যা করে না। উদাহরণস্বরূপ, ওজন 2 সহ কোনও আইটেম ওজন 1 সহ প্রথম হওয়ার সম্ভাব্য দ্বিগুণ নয়।

একটি তালিকা বদল করার একটি উপায় হ'ল তালিকার প্রতিটি উপাদানকে এলোমেলো সংখ্যা নির্ধারণ করা এবং সেই সংখ্যাগুলি অনুসারে বাছাই করা। আমরা এই ধারণাটি প্রসারিত করতে পারি, আমাদের কেবল ওজনযুক্ত এলোমেলো সংখ্যা বেছে নিতে হবে। উদাহরণস্বরূপ, আপনি ব্যবহার করতে পারেন random() * weight। বিভিন্ন পছন্দ বিভিন্ন বিতরণ উত্পাদন করবে।

পাইথনের মতো কিছুতে এটি এতটা সহজ হওয়া উচিত:

items.sort(key = lambda item: random.random() * item.weight)

সতর্কতা অবলম্বন করুন যে আপনি কীগুলি একবারে আর একবারে মূল্যায়ন করবেন না কারণ এগুলি বিভিন্ন মান সহ শেষ হবে।


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

ওজনের ওজন কত? এগুলি যদি উচ্চ হয় তবে অবজেক্টগুলি ওজন অনুসারে বাছাই করা হয়। যদি সেগুলি কম হয় তবে ওজন অনুসারে অবজেক্টগুলি প্রায় সামান্য প্রতিচ্ছবি নিয়ে প্রায় এলোমেলো। যেভাবেই হোক না কেন, এই পদ্ধতিটি আমি সর্বদা ব্যবহার করেছি, তবে বাছাই পজিশনের গণনার জন্য সম্ভবত কিছু টুইট করার প্রয়োজন হবে।
david.pfx

@ david.pfx ওজনের পরিসরটি এলোমেলো সংখ্যার ব্যাপ্তি হওয়া উচিত। এই ভাবে max*min = min*max, এবং এইভাবে কোন বিন্যাস সম্ভব, কিন্তু কিছু আরো অনেক কিছু সম্ভবত (বিশেষত যদি ওজন অবিশেষে বিস্তার না) হয়
নাথন মেরিল

2
আসলে, এই পদ্ধতির ভুল! 75 এবং 25 এর ওজন কল্পনা করুন 75৫ টি ক্ষেত্রে, সময়ের 2/3 সময় এটি একটি নম্বর> 25 নির্বাচন করবে the বাকি সময়টির 1/3 অংশের জন্য, সময়টি 25% হারে "বিট" করবে। 75 সময়ের প্রথম 2/3 + (1/3 * 1/2) হবে: 83%। এখনও ঠিক কাজ করা হয়নি।
আদম রাবাং

1
একটি দ্রষ্টব্য বিতরণ দ্বারা এলোমেলো নমুনার অভিন্ন বিতরণ প্রতিস্থাপন করে এই সমাধানটি কাজ করা উচিত।
পি-জিএন

5

প্রথমে, কাজটি করা যাক তালিকা অনুসারে প্রদত্ত একটি উপাদানটির ওজন স্থির রাখার জন্য। এটি পুনরাবৃত্তির মধ্যে পরিবর্তন করতে যাচ্ছে না। যদি তা হয়, তবে ... ভাল, এটি একটি বড় সমস্যা চালিয়ে যাবে।

উদাহরণের জন্য কার্ডগুলির একটি ডেক ব্যবহার করতে দেয় যেখানে আমরা সামনের দিকে মুখের কার্ডগুলি ওজন করতে চাই। weight(card) = card.rank। এগুলি সংক্ষেপে, আমরা যদি ওজনের বন্টনটি না জানি তবে প্রকৃতপক্ষে একবার ও (এন)।

এই উপাদানগুলি একটি বাছাই করা কাঠামোতে সংরক্ষণ করা হয় যেমন একটি সূচিযোগ্য স্কিপ তালিকার সংশোধন যেমন স্তরের সমস্ত সূচকগুলি একটি প্রদত্ত নোড থেকে অ্যাক্সেস করা যায়:

   1 10
 ও ---> ও -------------------------------------------- -------------> ও শীর্ষ স্তরের
   1 3 2 5
 ও ---> ও ---------------> ও ---------> ও ---------------- -----------> ও স্তর 3
   1 2 1 2 5
 ও ---> ও ---------> ও ---> ও ---------> ও ----------------- ----------> ও স্তর 2 2
   1 1 1 1 1 1 1 1 1 1 1 
 ও ---> ও ---> ও ---> ও ---> ও ---> ও ---> ও ---> ও ---> ও ---> ও ---> o ---> ও নীচের স্তর

শিরোনাম 1 ম দ্বিতীয় তৃতীয় 4 র্থ 5 ষ্ঠ 6 তম 8 ম 9 ম 10 ম শূন্যস্থান
      নোড নোড নোড নোড নোড নোড নোড নোড

তবে এই উদাহরণে, প্রতিটি নোড তার ওজনের যতটা জায়গা 'গ্রহণ' করে।

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

যেহেতু কোনও উপাদানটির ওজন স্থির থাকে sum -= weight(removed)তাই স্ট্রাকচারটি আবার অতিক্রম না করেই সহজেই করা যায়।

এবং এইভাবে, আপনি ও (এন) এর এক সময়ের মূল্য এবং ও (লগ এন) এর অনুসন্ধান মূল্য এবং ও (1) এর তালিকা ব্যয় থেকে একটি অপসারণ পেয়েছেন। এটি ও (এন) + এন * ও (লগ এন) + এন * ও (1) হয়ে যায় যা আপনাকে ও (এন লগ এন) এর সামগ্রিক কর্মক্ষমতা দেয় gives


কার্ডগুলির সাথে এটি দেখতে দিন, কারণ আমি উপরে যা ব্যবহার করেছি তা ঠিক তাই।

      10
শীর্ষ 3 -----------------------> 4 ডি
                                ।
       3 7।
    2 ---------> 2 ডি ---------> 4 দিন
                  । ।
       1 2। 3 4
বট 1 -> বিজ্ঞাপন -> 2 ডি -> 3 ডি -> 4 ডি

এটিতে কেবল 4 টি কার্ড সহ এটি একটি খুব ছোট ডেক। এটি কীভাবে বাড়ানো যায় তা দেখতে সহজ হওয়া উচিত। 52 টি কার্ডের সাথে একটি আদর্শ কাঠামোর 6 টি স্তর থাকবে (লগ 2 (52) ~ = 6), আপনি যদি স্কিপ তালিকাগুলিতে খনন করেন তবে এটিও কম সংখ্যায় কমে যেতে পারে।

সমস্ত ওজনের যোগফল 10 হয়। সুতরাং আপনি [1 .. 10) থেকে একটি এলোমেলো নম্বর পেয়েছেন এবং এর 4 টি আপনি সিলিং (4) এ থাকা আইটেমটি সন্ধান করতে স্কিপ তালিকায় চলে যান। 4 যেহেতু 10 এর চেয়ে কম, আপনি শীর্ষ স্তর থেকে দ্বিতীয় স্তরে চলে যান। চারটি 3 এর চেয়ে বেশি, সুতরাং এখন আমরা হীরার 2 তে আছি। 4 3 + 7 এর চেয়ে কম, সুতরাং আমরা নীচের স্তরে চলে যাই এবং 4 3 + 3 এর চেয়ে কম হয়, তাই আমরা 3 টি হীরক পেয়েছি।

কাঠামো থেকে 3 টি হীরার অপসারণের পরে, কাঠামোটি এখন এমন দেখাচ্ছে:

       7
শীর্ষ 3 ----------------> 4 ডি
                         ।
       3 4
    2 ---------> 2 ডি -> 4 ডি
                  । ।
       1 2। ঘ।
বট 1 -> বিজ্ঞাপন -> 2 ডি -> 4 ডি

আপনি লক্ষ করবেন যে নোডগুলি কাঠামোর মধ্যে তাদের ওজনের সাথে আনুপাতিক পরিমাণে 'স্থান' গ্রহণ করে। এটি ওজনযুক্ত নির্বাচনের জন্য অনুমতি দেয়।

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

এর বেশিরভাগ অংশটি একরকম সুষম গাছের সাহায্যে করা যেতে পারে। কাঠামোর পুনরায় ভারসাম্য দেখা দেয় যখন কোনও নোড সরানো হয় তখন বিভ্রান্ত হয়ে যায় যেহেতু এটি কোনও সর্বোত্তম গাছের কাঠামো নয় এবং গৃহকর্মী মনে রাখবেন যে হীরার ৪ টি এখন অবস্থান থেকে সরানো হয়েছে [7 7 8 9] [3 4 5 6] গাছ কাঠামোর সুবিধার চেয়ে বেশি খরচ হতে পারে cost

যাইহোক, যখন স্কিপ তালিকাটি বাইনারি গাছটিকে ও (লগ এন) সময়ে তালিকাটি বাদ দিতে পারে তার সান্নিধ্য হিসাবে, এর পরিবর্তে লিঙ্কযুক্ত তালিকার সাথে কাজ করার সরলতা রয়েছে।

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


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

1
@ এমআরটিআই এইভাবে একটি সূচকযোগ্য স্কিপ তালিকার ধারণার পরিবর্তন করে ification মূলটি সেই উপাদানটিতে অ্যাক্সেস করতে সক্ষম হবেন যেখানে ও ও (এন) সময়ের পরিবর্তে পূর্বের উপাদানগুলির ওজন <23 টি ও (লগ এন) সময়ে যোগ করা হয়। আপনি এখনও যেভাবে র্যান্ডম উপাদানটি বর্ণনা করছেন তা বাছাই করুন, [0, যোগফল (ওজন)] থেকে একটি এলোমেলো সংখ্যা নির্বাচন করুন এবং তারপরে তালিকা থেকে সংশ্লিষ্ট উপাদানটি পান। এড়িয়ে যাওয়া তালিকায় নোডগুলি / কার্ডগুলি কী অর্ডারে রয়েছে তা বিবেচ্য নয় - কারণ ভারী ওজনযুক্ত আইটেমগুলির দ্বারা নেওয়া বৃহত্তর 'স্পেস' মূল।

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