বাস্তব জগতের ঘটনা সিমুলেট করতে অনেক প্রোগ্রামেরই (আপাতদৃষ্টিতে) র্যান্ডম মান প্রয়োজন হয়।
কমন ও পরিচিত কিছু উদাহরণ:
('H', 'T') থেকে একটি র্যান্ডম মান।কম্পিউটার দিয়ে সত্যিকার অর্থে র্যান্ডম মান তৈরি করা একটি বিস্ময়কর কঠিন প্রযুক্তিগত চ্যালেঞ্জ, তাই এই ধরনের ফলাফলকে "সিউডোর্যান্ডম" বলা হতে পারে।
গুরুত্বপূর্ণ: এই কনসেপ্টে ক্রিপ্টোগ্রাফিকভাবে নিরাপদ র্যান্ডম সংখ্যা আলোচনা করা হয় না, যা আরও অনেক কঠিন একটি চ্যালেঞ্জ।
তবে Julia-র স্ট্যান্ডার্ড লাইব্রেরির Random মডিউলের মতো সুপরিকল্পিত লাইব্রেরিগুলো দ্রুত, নমনীয়, এবং মডেলিং, সিমুলেশন ও গেমে বেশিরভাগ প্রয়োগের জন্য যথেষ্ট ভালো ফলাফল দেয়।
Julia র্যান্ডম কার্যকারিতাকে একাধিক জায়গায় ভাগ করেছে:
Base-এ মাত্র কয়েকটি মৌলিক কিন্তু অত্যন্ত বহুমুখী ফাংশন, যা সবসময় পাওয়া যায়।Random মডিউলে আরও বিস্তৃত বিকল্প।Random স্ট্যান্ডার্ড লাইব্রেরির অংশ এবং সম্ভবত আগেই ইনস্টল করা থাকে, তবে এর বিষয়বস্তু নেমস্পেসে আনতে আপনার প্রোগ্রামের শুরুতে using Random যোগ করতে হবে।
rand() ফাংশনএই ফাংশনটি কী করবে তা নির্ভর করে আপনি তাকে কী আর্গুমেন্ট দিচ্ছেন তার উপর। এখানে অনেক সম্ভাবনা আছে।
কোনো আর্গুমেন্ট ছাড়া এটি ০ (সহ) থেকে ১-এর মধ্যে একটি ফ্লোট তৈরি করে।
এটি একটি uniform ডিস্ট্রিবিউশন, যেখানে সব মান সমানভাবে সম্ভাব্য, যা নিচে "ডিস্ট্রিবিউশন নিয়ে কাজ করা" অংশে আলোচনা করা হয়েছে।
একটি মাত্র ইন্টিজার আর্গুমেন্ট সেই দৈর্ঘ্যের একটি ভেক্টর তৈরি করে।
julia> rand()
0.10261774967264703
julia> rand(5)
5-element Vector{Float64}:
0.24134501977563894
0.5664193284851202
0.9804412082089355
0.6229551330613335
0.47589221741904664
ভিন্ন রেঞ্জের জন্য, ফলাফলটিকে যথাযথভাবে শিফট ও স্কেল করে নিন।
নিচের উদাহরণে বিয়োগের জন্য ব্রডকাস্টিং ব্যবহার করা হয়েছে, যা ভেক্টর অপারেশনস কনসেপ্টে আলোচনা করা হয়েছে।
.- কেবল প্রতিটি ভেক্টর এলিমেন্টে এই গাণিতিক কাজটি প্রয়োগ করে।
# numbers between -1.0 and +1.0
julia> (rand(5) .- 0.5) * 2
5-element Vector{Float64}:
-0.5303906759076336
0.9635682226775855
-0.048823697086981754
0.465842804648374
0.9880834344780736
কেবল একটি টাইপ আর্গুমেন্ট হিসেবে দিলে, rand সীমা হিসেবে typemin ও typemax ব্যবহার করবে।
সম্ভবত আপনি এটা চান না!
র্যান্ডম ইন্টিজারের জন্য আমরা একটি রেঞ্জ দিতে পারি, এবং চাইলে কতগুলো মান তৈরি করতে হবে তাও।
julia> rand(Int64)
-9159538335234594326 # not very useful
julia> rand(1:10, 5)
5-element Vector{Int64}:
1
1
1
4
7
উপরের rand(1:10, 5) উদাহরণে খেয়াল করুন, (কাকতালীয়ভাবে) কিছু মান পুনরাবৃত্তি হয়েছে, কারণ প্রতিটি পছন্দ স্বাধীন।
এটাই "রিপ্লেসমেন্ট-সহ স্যাম্পলিং", যা নিচে আরও বিস্তারিত আলোচনা করা হয়েছে।
একটি রেঞ্জে ফ্লোটিং-পয়েন্ট মানের জন্য সাধারণত আপনাকে স্টেপ সাইজ উল্লেখ করতে হবে। নাহলে স্টেপ ডিফল্টভাবে 1.0 হবে, যা খুব কম ক্ষেত্রেই কাজে লাগে।
julia> rand(2.4:0.01:3.2, 4)
4-element Vector{Float64}:
3.19
2.53
3.14
3.13
এর বদলে একটি অ্যারে বা টাপল দিলে, rand একটি র্যান্ডম এন্ট্রি রিটার্ন করবে:
julia> rand([4, 9, 16, 25])
16
# coin flip
julia> rand(['H', 'T'])
'H': ASCII/Unicode U+0048 (category Lu: Letter, uppercase)
# mixed types in tuple
julia> rand( (1, 3.2, "name"), 2 )
2-element Vector{Any}:
1
"name"
ভাবুন, আমাদের একটি থলেতে ৩টি লাল বল ও ৪টি সবুজ বল আছে, এবং আমরা থলেটি থেকে এলোমেলোভাবে একটি বল টানলাম। দ্বিতীয় বলটি পেতে দুটি সম্ভাবনা আছে:
১ নম্বর পরিস্থিতি রিপ্লেসমেন্ট-সহ, ২ নম্বরটি রিপ্লেসমেন্ট ছাড়া, এবং এরা ভিন্ন ফলাফল দেয়।
Julia-তে রিপ্লেসমেন্ট ছাড়া স্যাম্পলিং সিমুলেট করতে কয়েকটি উপায় আছে।
সবচেয়ে সহজ (এবং Exercism-এর মধ্যে একমাত্র উপায়), Random.shuffle() দিয়ে এন্ট্রিগুলোকে র্যান্ডম ক্রমে সাজান, তারপর প্রথম nটি এলিমেন্ট নিন।
ছোট সমস্যার জন্য এটি ঠিক আছে, তবে বড় কালেকশনে ভালোভাবে স্কেল নাও করতে পারে: আপনার সামান্য অংশই দরকার হলেও shuffle-কে পুরো অ্যারে তৈরি করতে হয়।
রিপ্লেসমেন্ট-সহ স্যাম্পলিং "ঠিকভাবে" করতে StatsBase.jl প্যাকেজটি ইনস্টল করুন।
এটি সম্পূর্ণ বিকল্পসহ sample() ফাংশন দেয়।
আমরা যুক্তিসঙ্গতভাবেই আশা করতে পারি, ভবিষ্যতের কোনো রিলিজে একই ধরনের কার্যকারিতা Random-এ যোগ করা হবে, যাতে এটি স্ট্যান্ডার্ড লাইব্রেরির অংশ হয়ে যায় (এই ডকুমেন্টের কোড নমুনাগুলো Julia 1.11 দিয়ে পরীক্ষা করা হয়েছে)।
এখন পর্যন্ত আমরা এমন ক্ষেত্রে মনোযোগ দিয়েছি যেখানে সব ফলাফল সমানভাবে সম্ভাব্য।
উদাহরণস্বরূপ, rand(1:100) ১ থেকে ১০০ পর্যন্ত যেকোনো ইন্টিজার সমান সম্ভাবনায় দিতে পারে।
বাস্তব জগতের অনেক পরিস্থিতি এর চেয়ে অনেক কম সরল।
ফলে পরিসংখ্যানবিদরা "বাস্তব জগতের" ফলাফল গাণিতিকভাবে বর্ণনা করতে নানা ধরনের distributions তৈরি করেছেন।
উপরে বর্ণিত rand() ফাংশনটি ব্যবহার করা হয় যখন সব সম্ভাবনা সমান।
এটাকে [uniform][uniform-distribution] ডিস্ট্রিবিউশন বলা হয়।
একে "নরমাল" ডিস্ট্রিবিউশন বা "ঘণ্টা-আকৃতির" কার্ভও বলা হয়; পরিমাপ করা মানের অনিশ্চয়তা বর্ণনা করার একটি খুব কমন উপায় এটি।
উদাহরণস্বরূপ, ধরুন আপনার কর্মস্থলের কারখানা এইমাত্র ১০,০০০টি বোল্ট কিনেছে, যেগুলো একই রকম হওয়ার কথা।
সেগুলো সামলানোর জন্য আপনি কারখানার রোবট সেটআপ করতে চান, তাই ১০০টির একটি নমুনা ওজন করে দেখলেন তাদের গড় (বা mean) ওজন ৪.৭৩১ গ্রাম।
এর মানে তারা সবাই ঠিক ৪.৭৩১ গ্রাম ওজনের, এমন হওয়ার সম্ভাবনা অত্যন্ত কম।
হয়তো দেখলেন মানগুলো ৪.৬২৭ থেকে ৪.৭৯৪ গ্রাম পর্যন্ত, তবে ৪.৭৩১ গ্রামের আশপাশে জড়ো হয়েছে।
এটাই Gaussian distribution, যেখানে সম্ভাবনা গড়ে সর্বোচ্চ হয় এবং দুই দিকে সমানভাবে কমতে থাকে (তাই "ঘণ্টা-আকৃতির")।
সফটওয়্যারে এটি সিমুলেট করতে কার্ভের প্রস্থ নির্দিষ্ট করার একটি উপায় দরকার (সাধারণত দামি বোল্ট সস্তা বোল্টের চেয়ে গড়ের চারপাশে বেশি ঘনভাবে জড়ো হয়!).
প্রথা অনুযায়ী এটি করা হয় standard deviation দিয়ে: ছোট মান মানে তীক্ষ্ণ, সরু কার্ভ; বড় মান মানে নিচু, চওড়া কার্ভ।
গণিতবিদরা গ্রিক অক্ষর ভালোবাসেন, তাই গড় বোঝাতে আমরা μ ('mu') এবং স্ট্যান্ডার্ড ডেভিয়েশন বোঝাতে σ ('sigma') ব্যবহার করি।
তাই আপনি যদি পড়েন "৯৫% মান μ-এর ২σ-এর মধ্যে থাকে" বা "হিগস বোসন ৫-সিগমা আত্মবিশ্বাসে শনাক্ত হয়েছে", এ ধরনের কথা স্ট্যান্ডার্ড ডেভিয়েশনের সাথে সম্পর্কিত।
এই বিষয়ে আরও কথা থাকবে Statistics কনসেপ্টে।
randn() ফাংশন"র্যান্ডম নরমাল"-এর সংক্ষিপ্ত রূপ, এটি rand()-এর ফ্লোটিং-পয়েন্ট রূপের মতোই, শুধু মানগুলো গড় ০ ও স্ট্যান্ডার্ড ডেভিয়েশন ১-এর গাউসিয়ান হিসেবে বিন্যস্ত হয়।
আবারও, স্ট্যান্ডার্ড ডেভিয়েশনের জন্য randn-এর কাঁচা আউটপুট স্কেল করতে চাইতে পারেন, এবং গড়ের জন্য তা সরণ করতে পারেন।
নিচের উদাহরণটি গড় ৩০ ও স্ট্যান্ডার্ড ডেভিয়েশন ৫-এ রূপান্তর করে।
julia> raw = randn(5)
5-element Vector{Float64}:
3.0762588867281475
1.5101100620253902
-0.5914858221637778
0.684175554069735
-0.8416433926114673
julia> raw * 5 .+ 30
5-element Vector{Float64}:
45.38129443364074
37.55055031012695
27.04257088918111
33.420877770348675
25.791783036942665
শুধু আউটপুট দেখে বোঝা কঠিন যে ইউনিফর্ম ডিস্ট্রিবিউশনের তুলনায় কাঁচা আউটপুট শূন্যের কাছাকাছি জড়ো হয়। সন্দেহ হলে ১০০০ বা তার বেশি মান তৈরি করে প্লট করুন, তাহলে আরও স্পষ্ট হবে।
Random মডিউলএই মডিউলটিতে পরবর্তী স্তরের কার্যকারিতা আছে, যা Julia-র ডিফল্ট কনফিগারেশনের আকার কমাতে Base থেকে বাদ দেওয়া হয়েছে।
Random-এ Base-এর rand ও randn-এর মিউটেটিং সংস্করণ rand! ও randn! যোগ করা হয়েছে।
একটি কাজের সংযোজন হলো randstring, যা নির্দিষ্ট দৈর্ঘ্যের একটি স্ট্রিং তৈরি করে।
ডিফল্টভাবে এটি ছোট-বড় হাতের অক্ষর এবং ০ থেকে ৯ পর্যন্ত অঙ্ক ব্যবহার করে, তবে অন্য পছন্দও উল্লেখ করা যায়।
julia> using Random
julia> randstring(20)
"BoJnIxrS33pJiWggXZQV"
এছাড়াও একটি bitrand ফাংশন আছে, যা নির্দিষ্ট দৈর্ঘ্যের একটি র্যান্ডম BitArray তৈরি করে।
julia> bitrand(5)
julia> bitrand(5)
5-element BitVector:
1
1
0
0
1
একটি Vector-এর এন্ট্রি র্যান্ডমভাবে শাফল করতে আছে shuffle; এছাড়া shuffle! আছে, যা ইনপুট ভেক্টরকে জায়গায় বসেই মিউটেট করে।
julia> v = ['A', '1', '2', 'J', 'Q', 'K'];
julia> shuffle(v)
6-element Vector{Char}:
'K': ASCII/Unicode U+004B (category Lu: Letter, uppercase)
'1': ASCII/Unicode U+0031 (category Nd: Number, decimal digit)
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'J': ASCII/Unicode U+004A (category Lu: Letter, uppercase)
'2': ASCII/Unicode U+0032 (category Nd: Number, decimal digit)
'Q': ASCII/Unicode U+0051 (category Lu: Letter, uppercase)
# shuffles are random:
julia> shuffle(v)
6-element Vector{Char}:
'2': ASCII/Unicode U+0032 (category Nd: Number, decimal digit)
'K': ASCII/Unicode U+004B (category Lu: Letter, uppercase)
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'Q': ASCII/Unicode U+0051 (category Lu: Letter, uppercase)
'J': ASCII/Unicode U+004A (category Lu: Letter, uppercase)
'1': ASCII/Unicode U+0031 (category Nd: Number, decimal digit)
কখনো কখনো শাফল করা ইনডেক্সগুলো থাকা কাজে লাগে।
এজন্য randperm(n) ব্যবহার করুন, যেখানে n হলো সিকোয়েন্সের দৈর্ঘ্য।
julia> randperm(6)
6-element Vector{Int64}:
6
2
4
1
3
5
প্রকৃতপক্ষে, উপরের উদাহরণটি shuffle(1:6)-এর মতোই ফলাফল দেয়।
সম্পর্কিত ফাংশনের মধ্যে আছে নির্দিষ্ট সম্ভাবনায় এন্ট্রি বেছে নেওয়ার জন্য randsubseq, এবং চক্রাকার পারমিউটেশনের জন্য randcycle।
এগুলোর জন্য কিছু বিশেষায়িত জ্ঞান দরকার, তাই আগ্রহ থাকলে ডকুমেন্টেশন দেখুন।
বিভিন্ন র্যান্ডম নাম্বার জেনারেটর (RNG) অ্যালগরিদম Random-এ স্ট্যান্ডার্ড হিসেবে অন্তর্ভুক্ত, এবং যথাযথ গাণিতিক দক্ষতা থাকলে যে কেউ আরও যোগ করতে পারেন।
এসব এই ডকুমেন্টের আলোচনার অনেক বাইরে!
RNG নিয়ে কাজ করার আরও কমন একটি কারণ হলো একটি seed নির্দিষ্ট করা, যার ফলে "র্যান্ডম" আউটপুটের ক্রম এক রান থেকে পরের রানে পুনরুৎপাদনযোগ্য হয়ে যায়।
এ ধরনের পুনরুৎপাদনযোগ্যতা প্রোডাকশন কোডে উপযুক্ত নয়, তবে টেস্টিং ও ডিবাগিংয়ে সাহায্য করতে পারে।
Exercism-এর বাইরে র্যান্ডমনেস, প্রব্যাবিলিটি ও স্ট্যাটিস্টিকস সম্পর্কিত অনেক ইনস্টলযোগ্য প্যাকেজ আছে।
আরও কিছু তথ্যের জন্য Statistics কনসেপ্ট দেখুন।
StatsBase.jl প্যাকেজStatsBase ফাংশনগুলোর বেশিরভাগই বেশ টেকনিক্যাল এবং এই ডকুমেন্টের সাথে প্রাসঙ্গিক নয়।
ব্যতিক্রম হলো StatsBase.sample, যা রিপ্লেসমেন্ট-সহ বা ছাড়া স্যাম্পলিংয়ের সম্পূর্ণ বাস্তবায়ন দেয় (উপরে আগের একটি অংশ দেখুন)।
ওয়েটেড (নন-ইউনিফর্ম) স্যাম্পলিংয়ের জন্যও ফাংশন আছে।
Distributions.jl প্যাকেজuniform ও normal (বা গাউসিয়ান) ডিস্ট্রিবিউশন উপরে বর্ণনা করা হয়েছে।
Random মডিউলে randexp-ও আছে, যা এক্সপোনেনশিয়াল ডিস্ট্রিবিউশন থেকে স্যাম্পল করে; এটি (খুব কমন) পয়সন ডিস্ট্রিবিউশন-এর সাথে সম্পর্কিত।
আরও অনেক বেশি বিকল্পের জন্য আছে Distributions.jl প্যাকেজ, যারা পরিসংখ্যানে যথাযথ পটভূমি রাখেন তাদের জন্য।