বাস্তব জগতের ঘটনা সিমুলেট করতে অনেক প্রোগ্রামেরই (আপাতদৃষ্টিতে) র্যান্ডম মান দরকার হয়।
কমন ও পরিচিত কিছু উদাহরণ:
('H', 'T') থেকে একটি র্যান্ডম মান।কম্পিউটার দিয়ে সত্যিকারের র্যান্ডম মান তৈরি করা আশ্চর্যজনকভাবে কঠিন একটি কারিগরি চ্যালেঞ্জ, তাই এই ফলাফলগুলোকে "সিউডোর্যান্ডম" বলা হতে পারে।
গুরুত্বপূর্ণ: এই কনসেপ্টে ক্রিপ্টোগ্রাফিক্যালি সুরক্ষিত র্যান্ডম সংখ্যা নিয়ে আলোচনা করা হয় না, যা আরও অনেক কঠিন একটি চ্যালেঞ্জ।
তবে জুলিয়া স্ট্যান্ডার্ড লাইব্রেরির Random মডিউলের মতো সুপরিকল্পিত লাইব্রেরিগুলো দ্রুতগতির ও নমনীয়, এবং মডেলিং, সিমুলেশন ও গেমে বেশিরভাগ অ্যাপ্লিকেশনের জন্য যথেষ্ট ভালো ফলাফল দেয়।
জুলিয়া র্যান্ডম সংক্রান্ত কার্যকারিতা কয়েকটি জায়গায় ভাগ করে রেখেছে:
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"
ভাবুন, আমাদের কাছে একটি ব্যাগ আছে যাতে ৩টি লাল বল আর ৪টি সবুজ বল আছে, এবং আমরা ব্যাগ থেকে র্যান্ডমভাবে একটি বল টানলাম। দ্বিতীয় একটি বল পেতে দুটি সম্ভাবনা আছে:
১ নম্বর দৃশ্যপট রিপ্লেসমেন্টসহ, ২ নম্বরটি ছাড়া, এবং এরা ভিন্ন ফলাফল দেয়।
জুলিয়ায় রিপ্লেসমেন্ট ছাড়া স্যাম্পলিং সিমুলেট করতে কয়েকটি বিকল্প আছে।
সবচেয়ে সহজ (এবং Exercism-এ একমাত্র বিকল্প) হলো Random.shuffle() দিয়ে এন্ট্রিগুলোকে র্যান্ডম ক্রমে সাজানো, তারপর প্রথম nটি এলিমেন্ট নেওয়া।
ছোট সমস্যার জন্য এটি ঠিক আছে, কিন্তু বড় কালেকশনে ভালোভাবে স্কেল নাও করতে পারে: shuffle-কে পুরো অ্যারে তৈরি করতে হয়, এমনকি আপনি এর ছোট একটি অংশ চাইলেও।
রিপ্লেসমেন্টসহ স্যাম্পলিং "ঠিকভাবে" করতে StatsBase.jl প্যাকেজটি ইনস্টল করুন।
এটি সম্পূর্ণ পরিসরের বিকল্পসহ sample() ফাংশন দেয়।
আমরা যুক্তিসঙ্গতভাবেই আশা করতে পারি যে ভবিষ্যতের কোনো রিলিজে অনুরূপ কার্যকারিতা Random-এ যোগ করা হবে, যাতে এটি স্ট্যান্ডার্ড লাইব্রেরির অংশ হয়ে যায় (এই ডকুমেন্টের কোডের নমুনাগুলো Julia 1.11 দিয়ে পরীক্ষা করা হয়েছে)।
এখন পর্যন্ত আমরা এমন ক্ষেত্রগুলোতেই মনোযোগ দিয়েছি যেখানে সব ফলাফলের সম্ভাবনা সমান।
উদাহরণস্বরূপ, rand(1:100) ১ থেকে ১০০ পর্যন্ত যেকোনো ইন্টিজার সমান সম্ভাবনায় দিতে পারে।
বাস্তব জগতের অনেক পরিস্থিতি এর চেয়ে অনেক কম সরল।
ফলে পরিসংখ্যানবিদরা "বাস্তব জগতের" ফলাফল গাণিতিকভাবে বর্ণনা করতে নানা ধরনের distributions তৈরি করেছেন।
উপরে বর্ণিত rand() ফাংশনটি ব্যবহার করা হয় যখন সব সম্ভাবনা সমান হয়।
এটিকে uniform ডিস্ট্রিবিউশন বলা হয়।
একে "নরমাল" ডিস্ট্রিবিউশন বা "ঘণ্টা-আকৃতির" কার্ভও বলা হয়; পরিমাপ করা মানের অনিশ্চয়তা বর্ণনা করার এটি খুবই কমন একটি উপায়।
উদাহরণস্বরূপ, ধরুন আপনি যে কারখানায় কাজ করেন সেখানে সদ্য ১০,০০০ বল্টু কেনা হয়েছে, যেগুলো একই রকম হওয়ার কথা।
আপনি সেগুলো সামলানোর জন্য কারখানার রোবট সেট আপ করতে চান, তাই ১০০টির একটি নমুনা ওজন করে দেখলেন তাদের গড় (বা 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 মডিউলএই মডিউলে পরবর্তী ধাপের কার্যকারিতা আছে, যা জুলিয়ার ডিফল্ট কনফিগারেশনের আকার ছোট রাখতে Base থেকে বাদ দেওয়া হয়েছে।
Random মডিউল Base-এর rand ও randn-এর সাথে মিউটেটিং সংস্করণ rand! ও randn! যোগ করে।
একটি কাজের সংযোজন হলো randstring, যা নির্দিষ্ট দৈর্ঘ্যের একটি স্ট্রিং তৈরি করে।
ডিফল্টভাবে এটি বড় ও ছোট হাতের অক্ষর এবং ০ থেকে ৯ পর্যন্ত অঙ্ক ব্যবহার করে, তবে অন্য পছন্দও উল্লেখ করা যায়।
julia> using Random
julia> randstring(20)
"BoJnIxrS33pJiWggXZQV"
এছাড়াও নির্দিষ্ট দৈর্ঘ্যের একটি র্যান্ডম BitArray তৈরি করার জন্য bitrand ফাংশন আছে।
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)-এর মতোই একই ফলাফল দেয়।
মেরি টিভি সিরিজ Star Trek: The Next Generation-এর একজন বড় ভক্ত। তিনি প্রায়ই কলম-কাগজে খেলা রোল প্লেয়িং গেম খেলেন, যেখানে তিনি আর তাঁর বন্ধুরা Starship Enterprise-এর ক্রু হওয়ার ভান করেন। মেরির চরিত্রটি ক্যাপ্টেন পিকার্ড, অর্থাৎ তাঁকে ক্যাপ্টেনের লগ রাখতে হয়। খেলার সৃজনশীল অংশটি তিনি ভালোবাসেন, কিন্তু মুহূর্তের মধ্যে র্যান্ডম ডেটা তৈরি করা পছন্দ করেন না।
ক্যাপ্টেনের লগে যেসব কমন ডেটা আসে, সেগুলোর জন্য র্যান্ডম জেনারেটর তৈরি করে মেরিকে সাহায্য করুন।
ভ্রমণের সময় Starship Enterprise অনেক গ্রহের দেখা পায়। Star Trek মহাবিশ্বের গ্রহগুলোকে তাদের বৈশিষ্ট্যের ভিত্তিতে বিভিন্ন শ্রেণিতে ভাগ করা হয়। যেমন, পৃথিবী একটি M শ্রেণির গ্রহ। সম্ভাব্য সব গ্রহ-শ্রেণি হলো: D, H, J, K, L, M, N, R, T এবং Y।
random_planet ফাংশনটি ইমপ্লিমেন্ট করুন। এটি র্যান্ডমভাবে গ্রহ-শ্রেণিগুলোর একটি রিটার্ন করবে।
julia> random_planet_class()
'K'
ঘুরে বেড়ানো একমাত্র স্টারশিপ Enterprise নয় (রেজিস্ট্রি নম্বর NCC-1701)! অন্য কোনো স্টারশিপের সাথে মিলিত হলে মেরিকে সেই স্টারশিপের রেজিস্ট্রি নম্বর লগ করতে হয়।
রেজিস্ট্রি নম্বর "NCC-" প্রিফিক্স দিয়ে শুরু হয়, তারপর 1000 থেকে 9999 (সহ) পর্যন্ত একটি সংখ্যা থাকে।
random_ship_registry_number ফাংশনটি ইমপ্লিমেন্ট করুন, যা একটি র্যান্ডম স্টারশিপ রেজিস্ট্রি নম্বর রিটার্ন করে।
julia> random_ship_registry_number()
"NCC-1947"
তারিখ না থাকলে লগের কী কাজ?
স্টারডেট একটি ফ্লোটিং পয়েন্ট সংখ্যা। The Next Generation-এর প্রথম সিজনের Starship Enterprise-এর অভিযানগুলো 41000.0 থেকে 42000.0 স্টারডেটের মধ্যে ঘটে। "4" মানে 24তম শতাব্দী, আর "1" মানে প্রথম সিজন।
random_stardate ফাংশনটি ইমপ্লিমেন্ট করুন, যা 41000.0 (সহ) থেকে 42000.0-এর মধ্যে একটি ফ্লোটিং পয়েন্ট সংখ্যা রিটার্ন করে।
julia> random_stardate()
41458.15721310934
স্টারডেটে অনেক বেশি দশমিক বিন্দু থাকার ব্যাপারে অভিযোগ এসেছে, তাই মেরি লগগুলোকে এক দশমিক স্থানে রাউন্ড করার সিদ্ধান্ত নেন।
random_stardate() থেকে পাওয়া মান রাউন্ড করেও এটি করা যেত, তবে আপনি বুঝতে পারেন যে এর অনেক সহজ একটি উপায় আছে।
random_stardate_v2 ফাংশনটি ইমপ্লিমেন্ট করুন, যা 41000.0 থেকে 42000.0-এর মধ্যে (সহ) একটি ফ্লোটিং পয়েন্ট সংখ্যা, এক দশমিক স্থানসহ, রিটার্ন করে।
julia> random_stardate_v2()
41732.6
খেলার একটি সংস্করণে অন্য স্টারশিপগুলোর সাথে যোগাযোগ করতে হয়, আর মেরির একটি নির্বাচন দরকার।
সম্ভাব্য স্টারশিপের একটি পূর্ণ তালিকা আছে, কিন্তু প্রতিটি খেলায় এর মধ্যে অল্প কয়েকটিই দরকার হয়, আর সেগুলো র্যান্ডমভাবে বেছে নিতে হয়।
pick_starships(starships, number_needed) ফাংশনটি ইমপ্লিমেন্ট করুন, যা অনন্য স্টারশিপ রেজিস্ট্রি নম্বরের একটি ভেক্টর নেয় এবং number_needed দৈর্ঘ্যের একটি ভেক্টর রিটার্ন করে। ফলাফলের মানগুলো starships থেকে নেওয়া হবে, কোনো ডুপ্লিকেট ছাড়াই।
julia> pick_starships(["NCC-5011", "NCC-1228", "NCC-7039", "NCC-3978", "NCC-1476"], 2)
2-element Vector{String}:
"NCC-1228"
"NCC-5011"
আমরা starships আর্গুমেন্টটিকে একটি ভেক্টর হিসেবে সীমাবদ্ধ করেছি।
টাপল বা সেটের মতো অন্য ইটারেবলগুলোর সাথেও কাজ করার জন্য আপনি pick_starships() কীভাবে ইমপ্লিমেন্ট করবেন?
এই অংশের জন্য কোনো টেস্ট নেই।
Exercism-এ সাইন আপ করুন, Julia ট্র্যাকের 35টি কনসেপ্ট128টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।