জেনেরিক তালিকা থেকে ৫ টি এলোমেলো উপাদান নির্বাচন করার জন্য আমার দ্রুত অ্যালগরিদম প্রয়োজন। উদাহরণস্বরূপ, আমি এ থেকে 5 টি এলোমেলো উপাদান পেতে চাই List<string>
।
জেনেরিক তালিকা থেকে ৫ টি এলোমেলো উপাদান নির্বাচন করার জন্য আমার দ্রুত অ্যালগরিদম প্রয়োজন। উদাহরণস্বরূপ, আমি এ থেকে 5 টি এলোমেলো উপাদান পেতে চাই List<string>
।
উত্তর:
প্রতিটি উপাদানের মাধ্যমে এবং এর জন্য আইট্রেট করুন নির্বাচনের সম্ভাব্যতাটি তৈরি করুন = (প্রয়োজনীয় নম্বর) / (নম্বরটি বাম)
সুতরাং আপনার যদি 40 টি আইটেম থাকে তবে প্রথমটির কাছে নির্বাচিত হওয়ার 5/40 সুযোগ থাকবে। যদি এটি হয় তবে পরেরটির কাছে 4/39 টির সুযোগ রয়েছে, অন্যথায় এটিতে 5/39 এর সুযোগ রয়েছে। আপনি শেষে পৌঁছানোর সময় আপনার কাছে আপনার 5 টি আইটেম থাকবে এবং প্রায়শই আপনার আগে সেগুলি হয়ে যাবে।
লিনাক ব্যবহার:
YourList.OrderBy(x => rnd.Next()).Take(5)
YourList
প্রচুর আইটেম রয়েছে কিনা তা দেখুন তবে আপনি কেবল কয়েকটি নির্বাচন করতে চান। এই ক্ষেত্রে এটি করার একটি দক্ষ উপায় নয়।
এটি যেমন মনে হয় ততই এটি একটি শক্ত সমস্যা, মূলত কারণ অনেকগুলি গাণিতিকভাবে সঠিক সমাধানগুলি আপনাকে সমস্ত সম্ভাবনাগুলিতে আঘাত করতে দেয় না (এটি নীচে আরও)।
প্রথমত, বাস্তবায়িত কিছু সহজ, সঠিক-যদি-আপনার-সত্য-র্যান্ডম-সংখ্যার জেনারেটর থাকে তবে:
(0) কাইলের উত্তর, যা ও (এন)।
(1) এন জোড়ার একটি তালিকা তৈরি করুন [(0, র্যান্ড), (1, র্যান্ড), (2, র্যান্ড), ...], তাদের দ্বিতীয় স্থানাঙ্ক অনুসারে বাছাই করুন এবং প্রথম কে ব্যবহার করুন (আপনার জন্য, কে = 5) সূচকগুলি আপনার এলোমেলো উপসেট পেতে। আমি মনে করি এটি কার্যকর করা সহজ, যদিও এটি ও (এন লগ এন) সময়।
(২) একটি খালি তালিকা শুরু করুন s = [] যা কে এলোমেলো উপাদানগুলির সূচক হিসাবে বাড়বে। Rand 0, 1, 2, ..., n-1 in এলোমেলোভাবে, r = র্যান্ড% n এ একটি নম্বর আর চয়ন করুন এবং এটিতে যোগ করুন। এরপরে আর = র্যান্ড% (n-1) নিন এবং এর মধ্যে থাকুন; সংঘর্ষ এড়াতে # টি উপাদানের চেয়ে কম উপাদান যুক্ত করুন। এরপরে আর = র্যান্ড% (এন -২) নিন এবং আপনার এস এর মধ্যে স্বতন্ত্র উপাদান না পাওয়া পর্যন্ত একই জিনিস ইত্যাদি করুন। এটি চলমান সময় হে (কে ^ 2) এর সবচেয়ে খারাপ ক্ষেত্রে রয়েছে। সুতরাং কে << এন এর জন্য এটি দ্রুত হতে পারে। যদি আপনি এইভাবে বাছাই করে রাখেন এবং এর সাথে কোন স্বতন্ত্র অন্তরগুলি ট্র্যাক করে থাকেন তবে আপনি এটিকে ও (কে লগ কে) এ প্রয়োগ করতে পারেন, তবে এটি আরও কাজ।
@ কাইল - ঠিক বলেছেন, দ্বিতীয় ভাবাতে আমি আপনার উত্তরের সাথে একমত হই। আমি তাড়াতাড়ি এটি প্রথমে পড়েছি এবং ভুল করে ভেবেছি আপনি স্থির সম্ভাব্য কে / এন দিয়ে প্রতিটি উপাদান যথাযথভাবে বেছে নেওয়ার ইঙ্গিত দিচ্ছেন, যা ভুল হত - তবে আপনার অভিযোজিত পদ্ধতিটি আমার কাছে সঠিক বলে মনে হচ্ছে। এর জন্যে দুঃখিত.
ঠিক আছে, এবং এখন কিকারের জন্য: asympototically (স্থির কে, এন বর্ধনের জন্য), সেখানে এন ^ কে / কে আছে! কে উপাদানগুলির পছন্দগুলি এন উপাদানগুলির মধ্যে উপসেট হয় [এটি একটি (এন নির্বাচন কে) এর একটি অনুমান)। যদি এন বড় হয় এবং কে খুব ছোট না হয় তবে এই সংখ্যাগুলি বিশাল। যে কোনও স্ট্যান্ডার্ড 32 বিট এলোমেলো সংখ্যা জেনারেটরের জন্য আপনি আশা করতে পারেন সর্বোত্তম চক্রের দৈর্ঘ্য হ'ল 2 ^ 32 = 256 ^ 4। সুতরাং যদি আমাদের কাছে 1000 উপাদানগুলির একটি তালিকা থাকে এবং আমরা এলোমেলোভাবে 5 টি চয়ন করতে চাই, তবে কোনও স্ট্যান্ডার্ড এলোমেলো সংখ্যা জেনারেটর সমস্ত সম্ভাবনা হিট করার উপায় নেই। তবে যতক্ষণ না আপনি ছোট্ট সেটগুলির জন্য সূক্ষ্মভাবে কাজ করে এমন কোনও পছন্দ দিয়ে ঠিক থাকেন এবং সর্বদা এলোমেলোভাবে "দেখায়" ততক্ষণ এই অ্যালগোরিদমগুলি ঠিক থাকা উচিত।
সংযোজন : এটি লেখার পরে, আমি বুঝতে পেরেছিলাম যে ধারণা (2) সঠিকভাবে প্রয়োগ করা কঠিন, সুতরাং আমি এই উত্তরটি পরিষ্কার করতে চেয়েছিলাম। ও (কে লগ কে) সময় পেতে আপনার একটি অ্যারের মতো কাঠামো দরকার যা ও (লগ এম) অনুসন্ধান এবং সন্নিবেশগুলিকে সমর্থন করে - একটি ভারসাম্য বাইনারি গাছ এটি করতে পারে। এস নামক একটি অ্যারে তৈরি করতে এ জাতীয় কাঠামো ব্যবহার করে এখানে কিছু সিউডোপथिথন দেওয়া হয়েছে:
# Returns a container s with k distinct random numbers from {0, 1, ..., n-1}
def ChooseRandomSubset(n, k):
for i in range(k):
r = UniformRandom(0, n-i) # May be 0, must be < n-i
q = s.FirstIndexSuchThat( s[q] - q > r ) # This is the search.
s.InsertInOrder(q ? r + q : r + len(s)) # Inserts right before q.
return s
আমি উপরের ইংরাজী ব্যাখ্যাটি কীভাবে দক্ষতার সাথে প্রয়োগ করে তা দেখতে কয়েকটি নমুনা কেস চালানোর পরামর্শ দিই।
আমি মনে করি নির্বাচিত উত্তরটি সঠিক এবং বেশ মিষ্টি। আমি যদিও এটি এলোমেলোভাবে ক্রম ফলাফল চেয়েছিলেন হিসাবে এটি অন্যভাবে বাস্তবায়িত।
static IEnumerable<SomeType> PickSomeInRandomOrder<SomeType>(
IEnumerable<SomeType> someTypes,
int maxCount)
{
Random random = new Random(DateTime.Now.Millisecond);
Dictionary<double, SomeType> randomSortTable = new Dictionary<double,SomeType>();
foreach(SomeType someType in someTypes)
randomSortTable[random.NextDouble()] = someType;
return randomSortTable.OrderBy(KVP => KVP.Key).Take(maxCount).Select(KVP => KVP.Value);
}
আমি কেবল এই সমস্যায় পড়েছি এবং আরও কিছু গুগল অনুসন্ধান আমাকে এলোমেলোভাবে একটি তালিকা বদলানোর সমস্যাটিতে নিয়ে এসেছিল: http://en.wikedia.org/wiki/Fisher- Yates_shuffle
সম্পূর্ণরূপে এলোমেলোভাবে আপনার তালিকাটি স্থান পরিবর্তন করতে (জায়গায়) আপনি এটি করুন:
একটি এন অ্যারের একটি অ্যারে পরিবর্তন করতে (সূচক 0..n-1):
for i from n − 1 downto 1 do
j ← random integer with 0 ≤ j ≤ i
exchange a[j] and a[i]
আপনার যদি কেবল প্রথম 5 টি উপাদান প্রয়োজন হয় তবে n-1 থেকে 1 পর্যন্ত সমস্ত পথ চালানোর পরিবর্তে আপনার কেবল এটি এন -5 চালানো উচিত (যেমন: এন -5)
বলুন আপনাকে কে আইটেমের প্রয়োজন হবে,
এটি হয়ে:
for (i = n − 1; i >= n-k; i--)
{
j = random integer with 0 ≤ j ≤ i
exchange a[j] and a[i]
}
নির্বাচিত প্রতিটি আইটেমটি অ্যারের শেষের দিকে অদলবদল করা হয়, সুতরাং নির্বাচিত কে উপাদানগুলি অ্যারের শেষ কে উপাদানগুলি।
এটি সময় নেবে O (কে), যেখানে k আপনার প্রয়োজনীয় এলোমেলোভাবে নির্বাচিত উপাদানগুলির সংখ্যা।
এছাড়াও, আপনি যদি আপনার প্রাথমিক তালিকাকে সংশোধন করতে না চান, আপনি নিজের সমস্ত অদল বদল অস্থায়ী তালিকায় লিখতে পারেন, সেই তালিকাটি আবার বিপরীত করতে পারেন এবং সেগুলি আবার প্রয়োগ করতে পারেন, এভাবে অদলবদলের বিপরীত সেট সম্পাদন করে এবং পরিবর্তন না করেই আপনাকে আপনার প্রাথমিক তালিকা ফিরিয়ে দিতে পারেন ও (কে) চলমান সময়।
অবশেষে, আসল স্টিকারারের জন্য, যদি (n == কে) হয় তবে আপনার এনকে নয়, 1 এ থামানো উচিত, কারণ এলোমেলোভাবে নির্বাচিত পূর্ণসংখ্যা সর্বদা 0 হবে।
আপনি এটি ব্যবহার করতে পারেন তবে ক্রম ক্লায়েন্টের পক্ষেই ঘটবে
.AsEnumerable().OrderBy(n => Guid.NewGuid()).Take(5);
অ্যালগরিদমে ড্রাগন থেকে , সি # তে একটি ব্যাখ্যা:
int k = 10; // items to select
var items = new List<int>(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 });
var selected = new List<int>();
double needed = k;
double available = items.Count;
var rand = new Random();
while (selected.Count < k) {
if( rand.NextDouble() < needed / available ) {
selected.Add(items[(int)available-1])
needed--;
}
available--;
}
এই অ্যালগরিদম আইটেম তালিকার অনন্য সূচি নির্বাচন করবে।
var
ফলাফলগুলি ব্যবহার করে needed
এবং available
উভয়ই পূর্ণসংখ্যা হয় যা needed/available
সর্বদা 0 হয়
একটি গ্রুপ থেকে এন এলোমেলো আইটেম নির্বাচন করার সাথে অর্ডার করার কিছু নেই ! এলোমেলোতা অদম্যতা সম্পর্কে এবং কোনও গ্রুপে অবস্থান বদলের বিষয়ে নয়। কিছু জবাব যা কিছু কিন্ডার অর্ডার নিয়ে কাজ করে সেগুলির তুলনায় কম দক্ষ হতে বাধ্য। দক্ষতা এখানে মূল কারণ, আমি এমন কিছু পোস্ট করব যা আইটেমগুলির ক্রমকে খুব বেশি পরিবর্তন করে না।
1) আপনার যদি সত্যিকারের এলোমেলো মানগুলির প্রয়োজন হয় যার অর্থ কোন উপাদানগুলির মধ্যে থেকে কোনটি বেছে নেওয়ার উপর কোনও বিধিনিষেধ নেই (যেমন, একবার নির্বাচিত আইটেমটি পুনরায় প্রকাশ করা যেতে পারে):
public static List<T> GetTrueRandom<T>(this IList<T> source, int count,
bool throwArgumentOutOfRangeException = true)
{
if (throwArgumentOutOfRangeException && count > source.Count)
throw new ArgumentOutOfRangeException();
var randoms = new List<T>(count);
randoms.AddRandomly(source, count);
return randoms;
}
আপনি যদি ব্যতিক্রম পতাকাটি অফ করে রাখেন, তবে আপনি যেকোন সংখ্যক বার এলোমেলো আইটেম চয়ন করতে পারেন।
আপনার যদি {1, 2, 3, 4 {থাকে, তবে এটি 3 আইটেমের জন্য এমনকি 1 ডলার, 4, 3, 2, 4} দিতে পারে, 1, 4, 4 4, {1, 4, 3} ইত্যাদি 5 টি আইটেম!
এটি বেশ দ্রুত হওয়া উচিত, কারণ এটির যাচাই করার মতো কিছুই নেই।
২) যদি কোনও পুনরাবৃত্তি না করে গোষ্ঠী থেকে পৃথক সদস্যের আপনার প্রয়োজন হয় , তবে আমি একটি অভিধানের উপর নির্ভর করব (যেমন অনেকে ইতিমধ্যে নির্দেশ করেছেন)।
public static List<T> GetDistinctRandom<T>(this IList<T> source, int count)
{
if (count > source.Count)
throw new ArgumentOutOfRangeException();
if (count == source.Count)
return new List<T>(source);
var sourceDict = source.ToIndexedDictionary();
if (count > source.Count / 2)
{
while (sourceDict.Count > count)
sourceDict.Remove(source.GetRandomIndex());
return sourceDict.Select(kvp => kvp.Value).ToList();
}
var randomDict = new Dictionary<int, T>(count);
while (randomDict.Count < count)
{
int key = source.GetRandomIndex();
if (!randomDict.ContainsKey(key))
randomDict.Add(key, sourceDict[key]);
}
return randomDict.Select(kvp => kvp.Value).ToList();
}
কোডটি এখানে অন্যান্য অভিধানের পদ্ধতির চেয়ে কিছুটা দীর্ঘতর কারণ আমি কেবল যুক্ত করছি না, তবে তালিকা থেকেও সরিয়ে দিচ্ছি, সুতরাং এর কিন্ডার দুটি লুপ রয়েছে। আপনি এখানে দেখতে পারেন যে, আমি নি পুনর্বিন্যস্তভাবে সব কিছু যখন count
সমান হয়ে source.Count
। এটা এ কারণে যে আমি বিশ্বাস করি যদৃচ্ছতা মধ্যে থাকা উচিত ফিরে সেট সামগ্রিকভাবে । আমি বলতে চাচ্ছি তুমি যদি চাও 5 র্যান্ডম আইটেম থেকে 1, 2, 3, 4, 5
, এটা ব্যাপার যদি তার করা উচিত নয় 1, 3, 4, 2, 5
বা 1, 2, 3, 4, 5
, কিন্তু যদি আপনি প্রয়োজন 4 একই সেট থেকে আইটেমগুলি, তাহলে এটি বিনা পূর্বাভাসেই মধ্যে উত্পাদ উচিত 1, 2, 3, 4
, 1, 3, 5, 2
, 2, 3, 5, 4
ইত্যাদি দ্বিতীয়ত, যখন র্যান্ডম আইটেম গণনা করা প্রত্যাবর্তনটি মূল গোষ্ঠীর অর্ধেকেরও বেশি, তারপরে এটি সরানো সহজsource.Count - count
আইটেম যোগ করার চেয়ে গ্রুপ থেকেcount
আইটেম। পারফরম্যান্সের কারণে আমি অপসারণ পদ্ধতিতে র্যান্ডম সূচক পাওয়ার source
পরিবর্তে ব্যবহার করেছি sourceDict
।
সুতরাং আপনার যদি {1, 2, 3, 4। থাকে তবে এটি 3 টি আইটেমের জন্য {1, 2, 3}, {3, 4, 1} ইত্যাদিতে শেষ হতে পারে।
3) যদি আপনার মূল গোষ্ঠীর নকলগুলি আমলে নিয়ে আপনার গোষ্ঠী থেকে সত্যই স্বতন্ত্র এলোমেলো মান প্রয়োজন হয় তবে আপনি উপরের মত একই পদ্ধতি ব্যবহার করতে পারেন, তবে HashSet
একটি অভিধানের চেয়ে হালকা হবে।
public static List<T> GetTrueDistinctRandom<T>(this IList<T> source, int count,
bool throwArgumentOutOfRangeException = true)
{
if (count > source.Count)
throw new ArgumentOutOfRangeException();
var set = new HashSet<T>(source);
if (throwArgumentOutOfRangeException && count > set.Count)
throw new ArgumentOutOfRangeException();
List<T> list = hash.ToList();
if (count >= set.Count)
return list;
if (count > set.Count / 2)
{
while (set.Count > count)
set.Remove(list.GetRandom());
return set.ToList();
}
var randoms = new HashSet<T>();
randoms.AddRandomly(list, count);
return randoms.ToList();
}
randoms
পরিবর্তনশীল একটি তৈরি করা হয় HashSet
এড়াতে সদৃশ বিরল ঘটনা ক্ষেত্রে যেখানে এর বিরল ঘটনা মধ্যে যোগ করা হচ্ছে Random.Next
একই মান উত্পাদ করতে পারেন, বিশেষত যখন ইনপুট তালিকা ছোট।
সুতরাং {1, 2, 2, 4} => 3 এলোমেলো আইটেম => {1, 2, 4} এবং কখনই {1, 2, 2}
{1, 2, 2, 4} => 4 এলোমেলো আইটেম => ব্যতিক্রম !! অথবা পতাকা সেটের উপর নির্ভর করে {1, 2, 4।।
কিছু এক্সটেনশন পদ্ধতি আমি ব্যবহার করেছি:
static Random rnd = new Random();
public static int GetRandomIndex<T>(this ICollection<T> source)
{
return rnd.Next(source.Count);
}
public static T GetRandom<T>(this IList<T> source)
{
return source[source.GetRandomIndex()];
}
static void AddRandomly<T>(this ICollection<T> toCol, IList<T> fromList, int count)
{
while (toCol.Count < count)
toCol.Add(fromList.GetRandom());
}
public static Dictionary<int, T> ToIndexedDictionary<T>(this IEnumerable<T> lst)
{
return lst.ToIndexedDictionary(t => t);
}
public static Dictionary<int, T> ToIndexedDictionary<S, T>(this IEnumerable<S> lst,
Func<S, T> valueSelector)
{
int index = -1;
return lst.ToDictionary(t => ++index, valueSelector);
}
যদি তালিকার দশটি আইটেমের দশ সহস্র গুণমান সম্পন্ন 10000 বার পুনরুক্তি করা উচিত, তবে আপনার তুলনায় দ্রুত এলোমেলো ক্লাস থাকতে পারে System.Random
তবে আমি সম্ভবত মনে করি না যে এটি সম্ভবত সবচেয়ে বড় বিষয় কখনও নয় বাধা, এটি যথেষ্ট দ্রুত ..
সম্পাদনা করুন: আপনার যদি ফেরত আইটেমগুলির ক্রমও আবার সাজানোর দরকার হয়, তবে hakাকিমের ফিশার-ইয়েটসের পদ্ধতিকে হারাতে পারে এমন কিছুই নেই - সংক্ষিপ্ত, মিষ্টি এবং সহজ ..
(প্যারাফ্রেজ) সম্পর্কিত গৃহীত উত্তরে @ জনশেডলেটস্কির মন্তব্য সম্পর্কে ভাবছিলেন :
আপনি ও (উপসেট.লেন্থ) এর চেয়ে ও (অরিজিনাল লিস্ট। দৈর্ঘ্য) এর পরিবর্তে সক্ষম হবেন
মূলত, আপনার উত্পন্ন করতে সক্ষম হওয়া উচিত subset
এলোমেলো সূচকগুলি এবং তারপরে এগুলি মূল তালিকা থেকে ছিনিয়ে নেওয়া উচিত।
public static class EnumerableExtensions {
public static Random randomizer = new Random(); // you'd ideally be able to replace this with whatever makes you comfortable
public static IEnumerable<T> GetRandom<T>(this IEnumerable<T> list, int numItems) {
return (list as T[] ?? list.ToArray()).GetRandom(numItems);
// because ReSharper whined about duplicate enumeration...
/*
items.Add(list.ElementAt(randomizer.Next(list.Count()))) ) numItems--;
*/
}
// just because the parentheses were getting confusing
public static IEnumerable<T> GetRandom<T>(this T[] list, int numItems) {
var items = new HashSet<T>(); // don't want to add the same item twice; otherwise use a list
while (numItems > 0 )
// if we successfully added it, move on
if( items.Add(list[randomizer.Next(list.Length)]) ) numItems--;
return items;
}
// and because it's really fun; note -- you may get repetition
public static IEnumerable<T> PluckRandomly<T>(this IEnumerable<T> list) {
while( true )
yield return list.ElementAt(randomizer.Next(list.Count()));
}
}
আপনি যদি আরও বেশি দক্ষ হতে চেয়েছিলেন, তাহলে আপনি সম্ভবত একটি ব্যবহার করেন HashSet
এর সূচকের , প্রকৃত তালিকা উপাদানের (যদি আপনি জটিল ধরনের বা ব্যয়বহুল তুলনা পেয়েছেন);
এবং আমাদের কোনও সংঘর্ষ নেই ইত্যাদি নিশ্চিত করার জন্য
[TestClass]
public class RandomizingTests : UnitTestBase {
[TestMethod]
public void GetRandomFromList() {
this.testGetRandomFromList((list, num) => list.GetRandom(num));
}
[TestMethod]
public void PluckRandomly() {
this.testGetRandomFromList((list, num) => list.PluckRandomly().Take(num), requireDistinct:false);
}
private void testGetRandomFromList(Func<IEnumerable<int>, int, IEnumerable<int>> methodToGetRandomItems, int numToTake = 10, int repetitions = 100000, bool requireDistinct = true) {
var items = Enumerable.Range(0, 100);
IEnumerable<int> randomItems = null;
while( repetitions-- > 0 ) {
randomItems = methodToGetRandomItems(items, numToTake);
Assert.AreEqual(numToTake, randomItems.Count(),
"Did not get expected number of items {0}; failed at {1} repetition--", numToTake, repetitions);
if(requireDistinct) Assert.AreEqual(numToTake, randomItems.Distinct().Count(),
"Collisions (non-unique values) found, failed at {0} repetition--", repetitions);
Assert.IsTrue(randomItems.All(o => items.Contains(o)),
"Some unknown values found; failed at {0} repetition--", repetitions);
}
}
}
আমি একটি লাজিলি-মূল্যায়িত এক্সটেনশন পদ্ধতি তৈরি করতে উপরের বেশ কয়েকটি উত্তর একত্রিত করেছি। আমার পরীক্ষায় দেখা গেছে যে কাইলের অ্যাপ্রোচ (অর্ডার (এন)) ড্রাজাসের দ্বারা নির্ধারিত র্যান্ডম সূচকগুলি (অর্ডার (কে)) প্রস্তাব দেওয়ার জন্য একটি সেট ব্যবহারের চেয়ে অনেকগুণ ধীর। প্রাক্তন এলোমেলো সংখ্যা জেনারেটরে আরও অনেক কল করে, এবং আইটেমগুলি দিয়ে আরও বারবার পুনরাবৃত্তি করে।
আমার বাস্তবায়নের লক্ষ্যগুলি ছিল:
1) কোনও আইএনলিস্টের কোনও আইলিস্ট না থাকলে পুরো তালিকাটি উপলব্ধি করবেন না। যদি আমাকে একটি জিলিয়ন আইটেমের ক্রম দেওয়া হয় তবে আমি স্মরণশক্তি ছাড়তে চাই না। অন-লাইন সমাধানের জন্য কাইলের পদ্ধতির ব্যবহার করুন।
2) যদি আমি বলতে পারি যে এটি একটি আইলিস্ট, তবে ড্রাজাসের অ্যাপ্রোচ ব্যবহার করুন, একটি মোচড় দিয়ে। কে যদি এন এর অর্ধেকের বেশি হয় তবে আমি বার বার অনেক এলোমেলো সূচকগুলি বেছে নিই এবং সেগুলি এড়িয়ে যেতে হবে বলে আমি ছিটকে যাওয়ার ঝুঁকি নিয়েছি। সুতরাং আমি সূচকগুলি না রাখার জন্য একটি তালিকা রচনা করেছি।
3) আমি গ্যারান্টি দিচ্ছি যে আইটেমগুলি যেভাবে सामना করেছিল সেগুলি একই ক্রমে ফিরে আসবে। কাইলের অ্যালগরিদমের কোনও পরিবর্তন দরকার হয়নি। ড্রজাউসের অ্যালগরিদমটির প্রয়োজন ছিল যে আমি এলোমেলো সূচকগুলি বেছে নেওয়া হয়েছে সেই ক্রমে আইটেমগুলি নির্গত করতে পারি না। আমি সমস্ত সূচকগুলি একটি সাজানোসেটে সংগ্রহ করি, তারপরে সাজানো সূচী ক্রমে আইটেমগুলি প্রেরণ করি।
৪) কে যদি এন এর সাথে তুলনা করে বড় হয় এবং আমি সেটটির ইন্দ্রিয়টি উল্টে রাখি, তবে আমি সমস্ত আইটেম গণনা করি এবং সূচকটি সেটে না থাকলে পরীক্ষা করি। এর অর্থ হ'ল আমি অর্ডার (কে) রানের সময়টি হারাব, তবে যেহেতু কে এই ক্ষেত্রে N এর নিকটবর্তী, তাই আমি খুব বেশি হারাতে পারি না।
কোডটি এখানে:
/// <summary>
/// Takes k elements from the next n elements at random, preserving their order.
///
/// If there are fewer than n elements in items, this may return fewer than k elements.
/// </summary>
/// <typeparam name="TElem">Type of element in the items collection.</typeparam>
/// <param name="items">Items to be randomly selected.</param>
/// <param name="k">Number of items to pick.</param>
/// <param name="n">Total number of items to choose from.
/// If the items collection contains more than this number, the extra members will be skipped.
/// If the items collection contains fewer than this number, it is possible that fewer than k items will be returned.</param>
/// <returns>Enumerable over the retained items.
///
/// See http://stackoverflow.com/questions/48087/select-a-random-n-elements-from-listt-in-c-sharp for the commentary.
/// </returns>
public static IEnumerable<TElem> TakeRandom<TElem>(this IEnumerable<TElem> items, int k, int n)
{
var r = new FastRandom();
var itemsList = items as IList<TElem>;
if (k >= n || (itemsList != null && k >= itemsList.Count))
foreach (var item in items) yield return item;
else
{
// If we have a list, we can infer more information and choose a better algorithm.
// When using an IList, this is about 7 times faster (on one benchmark)!
if (itemsList != null && k < n/2)
{
// Since we have a List, we can use an algorithm suitable for Lists.
// If there are fewer than n elements, reduce n.
n = Math.Min(n, itemsList.Count);
// This algorithm picks K index-values randomly and directly chooses those items to be selected.
// If k is more than half of n, then we will spend a fair amount of time thrashing, picking
// indices that we have already picked and having to try again.
var invertSet = k >= n/2;
var positions = invertSet ? (ISet<int>) new HashSet<int>() : (ISet<int>) new SortedSet<int>();
var numbersNeeded = invertSet ? n - k : k;
while (numbersNeeded > 0)
if (positions.Add(r.Next(0, n))) numbersNeeded--;
if (invertSet)
{
// positions contains all the indices of elements to Skip.
for (var itemIndex = 0; itemIndex < n; itemIndex++)
{
if (!positions.Contains(itemIndex))
yield return itemsList[itemIndex];
}
}
else
{
// positions contains all the indices of elements to Take.
foreach (var itemIndex in positions)
yield return itemsList[itemIndex];
}
}
else
{
// Since we do not have a list, we will use an online algorithm.
// This permits is to skip the rest as soon as we have enough items.
var found = 0;
var scanned = 0;
foreach (var item in items)
{
var rand = r.Next(0,n-scanned);
if (rand < k - found)
{
yield return item;
found++;
}
scanned++;
if (found >= k || scanned >= n)
break;
}
}
}
}
আমি একটি বিশেষায়িত র্যান্ডম নম্বর জেনারেটর ব্যবহার করি তবে আপনি চাইলে কেবল সি # র র্যান্ডম ব্যবহার করতে পারেন। ( ফাস্টআরন্ডমটি কলিন গ্রিন দ্বারা রচিত এবং শার্পনেট অংশ। এটির সময়কাল 2 ^ 128-1 যা অনেকগুলি আরএনজির চেয়ে ভাল)
এখানে ইউনিট পরীক্ষা রয়েছে:
[TestClass]
public class TakeRandomTests
{
/// <summary>
/// Ensure that when randomly choosing items from an array, all items are chosen with roughly equal probability.
/// </summary>
[TestMethod]
public void TakeRandom_Array_Uniformity()
{
const int numTrials = 2000000;
const int expectedCount = numTrials/20;
var timesChosen = new int[100];
var century = new int[100];
for (var i = 0; i < century.Length; i++)
century[i] = i;
for (var trial = 0; trial < numTrials; trial++)
{
foreach (var i in century.TakeRandom(5, 100))
timesChosen[i]++;
}
var avg = timesChosen.Average();
var max = timesChosen.Max();
var min = timesChosen.Min();
var allowedDifference = expectedCount/100;
AssertBetween(avg, expectedCount - 2, expectedCount + 2, "Average");
//AssertBetween(min, expectedCount - allowedDifference, expectedCount, "Min");
//AssertBetween(max, expectedCount, expectedCount + allowedDifference, "Max");
var countInRange = timesChosen.Count(i => i >= expectedCount - allowedDifference && i <= expectedCount + allowedDifference);
Assert.IsTrue(countInRange >= 90, String.Format("Not enough were in range: {0}", countInRange));
}
/// <summary>
/// Ensure that when randomly choosing items from an IEnumerable that is not an IList,
/// all items are chosen with roughly equal probability.
/// </summary>
[TestMethod]
public void TakeRandom_IEnumerable_Uniformity()
{
const int numTrials = 2000000;
const int expectedCount = numTrials / 20;
var timesChosen = new int[100];
for (var trial = 0; trial < numTrials; trial++)
{
foreach (var i in Range(0,100).TakeRandom(5, 100))
timesChosen[i]++;
}
var avg = timesChosen.Average();
var max = timesChosen.Max();
var min = timesChosen.Min();
var allowedDifference = expectedCount / 100;
var countInRange =
timesChosen.Count(i => i >= expectedCount - allowedDifference && i <= expectedCount + allowedDifference);
Assert.IsTrue(countInRange >= 90, String.Format("Not enough were in range: {0}", countInRange));
}
private IEnumerable<int> Range(int low, int count)
{
for (var i = low; i < low + count; i++)
yield return i;
}
private static void AssertBetween(int x, int low, int high, String message)
{
Assert.IsTrue(x > low, String.Format("Value {0} is less than lower limit of {1}. {2}", x, low, message));
Assert.IsTrue(x < high, String.Format("Value {0} is more than upper limit of {1}. {2}", x, high, message));
}
private static void AssertBetween(double x, double low, double high, String message)
{
Assert.IsTrue(x > low, String.Format("Value {0} is less than lower limit of {1}. {2}", x, low, message));
Assert.IsTrue(x < high, String.Format("Value {0} is more than upper limit of {1}. {2}", x, high, message));
}
}
if (itemsList != null && k < n/2)
অর্থ যা if
invertSet
সর্বদা অভ্যন্তরের অভ্যন্তরে থাকে false
যার অর্থ যে যুক্তি কখনও ব্যবহৃত হয় না।
@ এর এর উত্তর থেকে প্রসারিত, যদি কেউ অর্ডারবাইয়ের সম্ভাব্য বিভিন্ন প্রয়োগ সম্পর্কে উদ্বিগ্ন থাকে তবে এটি নিরাপদ হওয়া উচিত:
// Instead of this
YourList.OrderBy(x => rnd.Next()).Take(5)
// Temporarily transform
YourList
.Select(v => new {v, i = rnd.Next()}) // Associate a random index to each entry
.OrderBy(x => x.i).Take(5) // Sort by (at this point fixed) random index
.Select(x => x.v); // Go back to enumerable of entry
এটিই প্রথম যেটি আমি প্রথম কাটতে পেরেছি তা সবচেয়ে ভাল:
public List<String> getRandomItemsFromList(int returnCount, List<String> list)
{
List<String> returnList = new List<String>();
Dictionary<int, int> randoms = new Dictionary<int, int>();
while (randoms.Count != returnCount)
{
//generate new random between one and total list count
int randomInt = new Random().Next(list.Count);
// store this in dictionary to ensure uniqueness
try
{
randoms.Add(randomInt, randomInt);
}
catch (ArgumentException aex)
{
Console.Write(aex.Message);
} //we can assume this element exists in the dictonary already
//check for randoms length and then iterate through the original list
//adding items we select via random to the return list
if (randoms.Count == returnCount)
{
foreach (int key in randoms.Keys)
returnList.Add(list[randoms[key]]);
break; //break out of _while_ loop
}
}
return returnList;
}
1 - মোট তালিকার গণনা এবং এরপরে র্যান্ডমগুলির একটি তালিকা ব্যবহার করে তালিকায় সেই আইটেমগুলি টানাই সেরা উপায় বলে মনে হয়, তবে স্বাতন্ত্র্যতা নিশ্চিত করতে অভিধান ব্যবহার করা এমন কিছু যা আমি এখনও ম্লান করছি।
এছাড়াও নোট করুন আমি একটি স্ট্রিং তালিকা ব্যবহার করেছি, প্রয়োজন অনুযায়ী প্রতিস্থাপন করুন।
আমি যে সহজ সমাধানটি ব্যবহার করি (বৃহত তালিকার পক্ষে সম্ভবত এটি ভাল নয়): অস্থায়ী তালিকায় তালিকাটি অনুলিপি করুন, তারপরে লুপে এলোমেলোভাবে টেম্প তালিকা থেকে আইটেমটি নির্বাচন করুন এবং এটি অস্থায়ী তালিকা তৈরি করার সময় নির্বাচিত আইটেমের তালিকায় রাখুন (সুতরাং এটি হতে পারে না) reselected)।
উদাহরণ:
List<Object> temp = OriginalList.ToList();
List<Object> selectedItems = new List<Object>();
Random rnd = new Random();
Object o;
int i = 0;
while (i < NumberOfSelectedItems)
{
o = temp[rnd.Next(temp.Count)];
selectedItems.Add(o);
temp.Remove(o);
i++;
}
জন শেডলেটস্কি উল্লেখ করেছেন যে এখানে ফিশার-ইয়েটস সাফলের উপর ভিত্তি করে আপনার একটি বাস্তবায়ন রয়েছে যার অ্যালগরিদম জটিলতা হ'ল (এন) যেখানে এন সাবসেট বা নমুনা আকারের পরিবর্তে তালিকার আকারের পরিবর্তে জন শেডলেটস্কি উল্লেখ করেছেন।
public static IEnumerable<T> GetRandomSample<T>(this IList<T> list, int sampleSize)
{
if (list == null) throw new ArgumentNullException("list");
if (sampleSize > list.Count) throw new ArgumentException("sampleSize may not be greater than list count", "sampleSize");
var indices = new Dictionary<int, int>(); int index;
var rnd = new Random();
for (int i = 0; i < sampleSize; i++)
{
int j = rnd.Next(i, list.Count);
if (!indices.TryGetValue(j, out index)) index = j;
yield return list[index];
if (!indices.TryGetValue(i, out index)) index = i;
indices[j] = index;
}
}
কাইলের উত্তরের ভিত্তিতে, এখানে আমার সি # বাস্তবায়ন।
/// <summary>
/// Picks random selection of available game ID's
/// </summary>
private static List<int> GetRandomGameIDs(int count)
{
var gameIDs = (int[])HttpContext.Current.Application["NonDeletedArcadeGameIDs"];
var totalGameIDs = gameIDs.Count();
if (count > totalGameIDs) count = totalGameIDs;
var rnd = new Random();
var leftToPick = count;
var itemsLeft = totalGameIDs;
var arrPickIndex = 0;
var returnIDs = new List<int>();
while (leftToPick > 0)
{
if (rnd.Next(0, itemsLeft) < leftToPick)
{
returnIDs .Add(gameIDs[arrPickIndex]);
leftToPick--;
}
arrPickIndex++;
itemsLeft--;
}
return returnIDs ;
}
এই পদ্ধতিটি কাইলের সমতুল্য হতে পারে।
বলুন যে আপনার তালিকাটি আকারের এন এবং আপনি কে উপাদান চান।
Random rand = new Random();
for(int i = 0; k>0; ++i)
{
int r = rand.Next(0, n-i);
if(r<k)
{
//include element i
k--;
}
}
একটি যাদুমন্ত্র মত কাজ করে :)
-আলেক্স গিলবার্ট
কেন এই জাতীয় কিছু না:
Dim ar As New ArrayList
Dim numToGet As Integer = 5
'hard code just to test
ar.Add("12")
ar.Add("11")
ar.Add("10")
ar.Add("15")
ar.Add("16")
ar.Add("17")
Dim randomListOfProductIds As New ArrayList
Dim toAdd As String = ""
For i = 0 To numToGet - 1
toAdd = ar(CInt((ar.Count - 1) * Rnd()))
randomListOfProductIds.Add(toAdd)
'remove from id list
ar.Remove(toAdd)
Next
'sorry i'm lazy and have to write vb at work :( and didn't feel like converting to c#
এটি মনে করার চেয়ে অনেক কঠিন। জেফের দুর্দান্ত আর্টিকেল "শাফলিং" দেখুন ।
আমি সি # কোড সহ সেই বিষয়ে খুব ছোট একটি নিবন্ধ লিখেছিলাম :
প্রদত্ত অ্যারের N উপাদানগুলির এলোমেলো উপসেটটি ফিরিয়ে দিন
লক্ষ্য: নকল ছাড়াই সংগ্রহের উত্স থেকে N নম্বর আইটেম নির্বাচন করুন। আমি যে কোনও জেনেরিক সংগ্রহের জন্য একটি এক্সটেনশন তৈরি করেছি। আমি এটি কীভাবে করেছি তা এখানে:
public static class CollectionExtension
{
public static IList<TSource> RandomizeCollection<TSource>(this IList<TSource> source, int maxItems)
{
int randomCount = source.Count > maxItems ? maxItems : source.Count;
int?[] randomizedIndices = new int?[randomCount];
Random random = new Random();
for (int i = 0; i < randomizedIndices.Length; i++)
{
int randomResult = -1;
while (randomizedIndices.Contains((randomResult = random.Next(0, source.Count))))
{
//0 -> since all list starts from index 0; source.Count -> maximum number of items that can be randomize
//continue looping while the generated random number is already in the list of randomizedIndices
}
randomizedIndices[i] = randomResult;
}
IList<TSource> result = new List<TSource>();
foreach (int index in randomizedIndices)
result.Add(source.ElementAt(index));
return result;
}
}
আমি সম্প্রতি টাইলারের পয়েন্ট 1 এর অনুরূপ ধারণা ব্যবহার করে আমার প্রকল্পে এটি করেছি ।
আমি প্রচুর প্রশ্ন লোড করছিলাম এবং এলোমেলোভাবে পাঁচটি নির্বাচন করছিলাম। বাছাই একটি আইসি কম্পিউটারে ব্যবহার করে অর্জিত হয়েছিল ।
সমস্ত প্রশ্ন প্রশ্নাবলীর তালিকায় লোড হয়েছিল, তারপরে তালিকার ক্রম ফাংশন এবং নির্বাচিত প্রথম কে উপাদানগুলির সাহায্যে বাছাই করা হয়েছিল।
private class QuestionSorter : IComparable<QuestionSorter>
{
public double SortingKey
{
get;
set;
}
public Question QuestionObject
{
get;
set;
}
public QuestionSorter(Question q)
{
this.SortingKey = RandomNumberGenerator.RandomDouble;
this.QuestionObject = q;
}
public int CompareTo(QuestionSorter other)
{
if (this.SortingKey < other.SortingKey)
{
return -1;
}
else if (this.SortingKey > other.SortingKey)
{
return 1;
}
else
{
return 0;
}
}
}
ব্যবহার:
List<QuestionSorter> unsortedQuestions = new List<QuestionSorter>();
// add the questions here
unsortedQuestions.Sort(unsortedQuestions as IComparer<QuestionSorter>);
// select the first k elements
এখানে আমার পদ্ধতির (সম্পূর্ণ পাঠ্য এখানে http://krkadev.blogspot.com/2010/08/random-numbers-without-repetition.html )।
এটি ও (এন) এর পরিবর্তে ও (কে) এ চালানো উচিত, যেখানে কে ওয়ান্টেড এলিমেন্টের সংখ্যা এবং এনটি তালিকা থেকে পছন্দ করে নিন:
public <T> List<T> take(List<T> source, int k) {
int n = source.size();
if (k > n) {
throw new IllegalStateException(
"Can not take " + k +
" elements from a list with " + n +
" elements");
}
List<T> result = new ArrayList<T>(k);
Map<Integer,Integer> used = new HashMap<Integer,Integer>();
int metric = 0;
for (int i = 0; i < k; i++) {
int off = random.nextInt(n - i);
while (true) {
metric++;
Integer redirect = used.put(off, n - i - 1);
if (redirect == null) {
break;
}
off = redirect;
}
result.add(source.get(off));
}
assert metric <= 2*k;
return result;
}
আমি একটি এক্সটেনশন পদ্ধতি ব্যবহার করব।
public static IEnumerable<T> TakeRandom<T>(this IEnumerable<T> elements, int countToTake)
{
var random = new Random();
var internalList = elements.ToList();
var selected = new List<T>();
for (var i = 0; i < countToTake; ++i)
{
var next = random.Next(0, internalList.Count - selected.Count);
selected.Add(internalList[next]);
internalList[next] = internalList[internalList.Count - selected.Count];
}
return selected;
}
public static IEnumerable<T> GetRandom<T>(this IList<T> list, int count, Random random)
{
// Probably you should throw exception if count > list.Count
count = Math.Min(list.Count, count);
var selectedIndices = new SortedSet<int>();
// Random upper bound
int randomMax = list.Count - 1;
while (selectedIndices.Count < count)
{
int randomIndex = random.Next(0, randomMax);
// skip over already selected indeces
foreach (var selectedIndex in selectedIndices)
if (selectedIndex <= randomIndex)
++randomIndex;
else
break;
yield return list[randomIndex];
selectedIndices.Add(randomIndex);
--randomMax;
}
}
স্মৃতি: ~ গণনা
জটিলতা: হে (গণনা 2 )
যখন এন খুব বড় হয়, স্বাভাবিক পদ্ধতি যা এলোমেলোভাবে এন সংখ্যাগুলিকে বদল করে এবং নির্বাচন করে, বলে, প্রথম কে সংখ্যাগুলি, স্থান জটিলতার কারণে নিষিদ্ধ হতে পারে। নিম্নলিখিত অ্যালগরিদমের সময় ও স্থান উভয়ই জটিলতার জন্য কেবল ও (কে) প্রয়োজন।
http://arxiv.org/abs/1512.00501
def random_selection_indices(num_samples, N):
modified_entries = {}
seq = []
for n in xrange(num_samples):
i = N - n - 1
j = random.randrange(i)
# swap a[j] and a[i]
a_j = modified_entries[j] if j in modified_entries else j
a_i = modified_entries[i] if i in modified_entries else i
if a_i != j:
modified_entries[j] = a_i
elif j in modified_entries: # no need to store the modified value if it is the same as index
modified_entries.pop(j)
if a_j != i:
modified_entries[i] = a_j
elif i in modified_entries: # no need to store the modified value if it is the same as index
modified_entries.pop(i)
seq.append(a_j)
return seq
বড় তালিকাগুলি সহ লাইনকিউ ব্যবহার করা (যখন প্রতিটি উপাদানকে স্পর্শ করা ব্যয়বহুল) এবং যদি আপনি নকলের সম্ভাবনা নিয়ে বেঁচে থাকতে পারেন:
new int[5].Select(o => (int)(rnd.NextDouble() * maxIndex)).Select(i => YourIEnum.ElementAt(i))
আমার ব্যবহারের জন্য আমার 100.000 উপাদানগুলির একটি তালিকা ছিল এবং আমি তাদের পুরো তালিকায় একটি রেন্ডের তুলনায় সময়টিকে প্রায় অর্ধেক (বা আরও ভাল) ডিবি থেকে টেনে আছি বলে।
একটি বৃহত তালিকা থাকা নকলের পক্ষে প্রতিকূলতাকে হ্রাস করবে।
এটি আপনার সমস্যার সমাধান করবে
var entries=new List<T>();
var selectedItems = new List<T>();
for (var i = 0; i !=10; i++)
{
var rdm = new Random().Next(entries.Count);
while (selectedItems.Contains(entries[rdm]))
rdm = new Random().Next(entries.Count);
selectedItems.Add(entries[rdm]);
}