আমি কিছুক্ষণের জন্য ঝোপের চারপাশে মারতে যাচ্ছি, তবে একটি বিষয় আছে point
Semigroups
উত্তরটি হ'ল বাইনারি হ্রাস অপারেশনটির সহযোগী সম্পত্তি ।
এটি বেশ বিমূর্ত, তবে গুণ একটি ভাল উদাহরণ। তাহলে এক্স , Y এবং z- র কিছু স্বাভাবিক সংখ্যার (অথবা পূর্ণসংখ্যার, অথবা মূলদ সংখ্যা বা বাস্তব সংখ্যা বা জটিল সংখ্যা বা হয় এন × এন ম্যাট্রিক্স, অথবা একটি আভা কোন টি জিনিস), তারপর এক্স × Y একই ধরনের উভয় x এবং y হিসাবে সংখ্যা । আমরা দুটি সংখ্যা দিয়ে শুরু করেছি, সুতরাং এটি একটি বাইনারি অপারেশন, এবং একটি পেয়েছিলাম, তাই আমরা একের সাথে আমাদের সংখ্যার গণনা হ্রাস করেছি, এটি এটিকে হ্রাস অপারেশন করে। এবং ( x × y ) × z সর্বদা x × ( y ×) এর মতো হয় ×z ), যা সহযোগী সম্পত্তি।
(আপনি যদি ইতিমধ্যে এই সমস্ত কিছু জানেন তবে আপনি পরবর্তী বিভাগে যেতে পারেন))
কম্পিউটার বিজ্ঞানে আপনি আরও কয়েকটি জিনিস দেখতে পান যা একইভাবে কাজ করে:
- গুণনের পরিবর্তে এই ধরণের সংখ্যার যেকোনটি যুক্ত করা
- কানাট্যান্টিং স্ট্রিং (
"a"+"b"+"c"
এটি "abc"
আপনি শুরু করেন "ab"+"c"
বা না "a"+"bc"
)
- দুটি তালিকা একসাথে স্প্লাইক করা।
[a]++[b]++[c]
একইভাবে [a,b,c]
হয় পিছন থেকে সামনের দিকে বা সামনে থেকে পিছনে।
cons
একটি মাথা এবং লেজ নেভিগেশন, যদি আপনি মাথা একক তালিকা হিসাবে মনে হয়। এটি কেবল দুটি তালিকাকে সম্মতি জানানো।
- ইউনিয়ন বা সেট ছেদ করা
- বুলিয়ান এবং, বুলিয়ান বা
- বিটওয়াইজ
&
, |
এবং^
- ফাংশনগুলির সংমিশ্রণ: ( f ∘ g ) ∘ h x = f ∘ ( g ∘ h ) x = f ( g ( h ( x )))
- সর্বোচ্চ এবং সর্বনিম্ন
- সংযোজন মডুলো পি
কিছু জিনিস যা না:
- বিয়োগফল, কারণ 1- (1-2) ≠ (1-1) -2
- x ⊕ y = ট্যান ( x + y ), কারণ ট্যান (π / 4 + π / 4) অপরিবর্তিত
- negativeণাত্মক সংখ্যার উপর গুণ কারণ, -1 × -1 একটি নেতিবাচক সংখ্যা নয়
- তিনটি সমস্যা আছে যা পূর্ণসংখ্যার বিভাজন!
- যৌক্তিক নয়, কারণ এটির একটি মাত্র অপারেন্ড রয়েছে, দুটি নয়
int print2(int x, int y) { return printf( "%d %d\n", x, y ); }
হিসাবে print2( print2(x,y), z );
এবং print2( x, print2(y,z) );
বিভিন্ন আউটপুট আছে।
এটি একটি দরকারী যথেষ্ট ধারণা যা আমরা এটির নামকরণ করেছি। এই বৈশিষ্ট্যগুলি সহ একটি অপারেশন সহ একটি সেট হল একটি আধাগোষ্ঠী । সুতরাং, গুণনের অধীনে আসল সংখ্যাগুলি হল একটি আধাগোষ্ঠী। এবং আপনার প্রশ্নটি সত্যিকারের বিশ্বে এই ধরণের বিমূর্ততা কার্যকর হওয়ার অন্যতম উপায় হয়ে দাঁড়িয়েছে। আপনি যেভাবে জিজ্ঞাসা করছেন সেমিগ্রুপ ক্রিয়াকলাপগুলি সমস্তই অনুকূলিত হতে পারে।
বাড়িতে এটি চেষ্টা করুন
যতদূর আমি জানি, এই কৌশলটি প্রথম 1974 সালে ড্যানিয়েল ফ্রেডম্যান এবং ডেভিড ওয়াইজের গবেষণাপত্রে "ভাঁজ স্টাইলাইজড রিকার্সনগুলিকে আইট্রেসনেস" হিসাবে বর্ণনা করা হয়েছিল , যদিও তারা প্রয়োজনীয়তার চেয়ে আরও কয়েকটি সম্পত্তি ধরে নিয়েছিল ।
এটি চিত্রিত করার জন্য হাস্কেল একটি দুর্দান্ত ভাষা, কারণ Semigroup
এটির স্ট্যান্ডার্ড লাইব্রেরিতে টাইপক্লাস রয়েছে। এটি জেনেরিক Semigroup
অপারেটরের অপারেশনকে কল করে <>
। যেহেতু তালিকাগুলি এবং স্ট্রিংগুলি উদাহরণস্বরূপ Semigroup
, উদাহরণস্বরূপ তাদের দৃষ্টান্তগুলি সংক্ষিপ্ত <>
অপারেটর হিসাবে সংজ্ঞায়িত করা হয় ++
। এবং সঠিক আমদানি সহ, [a] <> [b]
একটি উপনাম [a] ++ [b]
, যা এটি [a,b]
।
তবে, সংখ্যা কী? আমরা কেবল দেখেছি যে সংখ্যার প্রকারগুলি হ'ল যোগ বা গুণফলের আওতায় সেমিগ্রুপ হয় ! যা এক হতে পায় একটি জন্য ? ভাল, হয় এক! Haskell, ধরনের সংজ্ঞায়িত , (যে Haskell, প্রকৃত সংজ্ঞা নেই), এবং এছাড়াও , ।<>
Double
Product Double
where (<>) = (*)
Sum Double
where (<>) = (+)
একটি বলি হল যে আপনি এই সত্যটি ব্যবহার করেছেন যে 1 হ'ল গুণগত পরিচয়। পরিচয়ের সাথে একটি সেমিগ্রুপকে মনোয়েড বলা হয় এবং এটি হাস্কেল প্যাকেজে সংজ্ঞায়িত হয় Data.Monoid
, যা টাইপক্লাসের জেনেরিক পরিচয় উপাদান বলে mempty
। Sum
, Product
এবং তালিকা প্রতিটি একটি পরিচয় উপাদান (0, 1 এবং আছে []
যথাক্রমে), তাই তারা উদাহরণ স্বরূপ ধরে Monoid
সেইসাথে Semigroup
। (একটি মোনাডের সাথে বিভ্রান্ত হওয়ার দরকার নেই , তাই ভুলে যান আমি এমনকি এগুলিও এনেছি))
মনোয়েডস ব্যবহার করে আপনার অ্যালগরিদমকে হাস্কেল ফাংশনে অনুবাদ করার জন্য এটি যথেষ্ট তথ্য:
module StylizedRec (pow) where
import Data.Monoid as DM
pow :: Monoid a => a -> Word -> a
{- Applies the monoidal operation of the type of x, whatever that is, by
- itself n times. This is already in Haskell as Data.Monoid.mtimes, but
- let’s write it out as an example.
-}
pow _ 0 = mempty -- Special case: Return the nullary product.
pow x 1 = x -- The base case.
pow x n = x <> (pow x (n-1)) -- The recursive case.
গুরুত্বপূর্ণভাবে লক্ষ করুন যে এটি পুচ্ছ পুনরাবৃত্তি মডুলো সেমিগ্রুপ: প্রতিটি ক্ষেত্রে হয় হয় একটি মান, একটি লেজ-পুনরাবৃত্তি কল বা উভয়ের আধাগোষ্ঠী পণ্য। এছাড়াও, এই উদাহরণটি mempty
একটির ক্ষেত্রে ব্যবহার করার জন্য ঘটেছে , তবে আমাদের যদি এটির দরকার না হত, আমরা আরও সাধারণ টাইপক্লাস দিয়ে এটি করতে পারতাম Semigroup
।
এই প্রোগ্রামটি GHCI এ লোড করুন এবং এটি কীভাবে কাজ করে তা দেখুন:
*StylizedRec> getProduct $ pow 2 4
16
*StylizedRec> getProduct $ pow 7 2
49
মনে রাখবেন কীভাবে আমরা জেনেরিকের pow
জন্য ঘোষনা করেছি Monoid
, যার ধরণের নাম আমরা ডাকলাম a
? আমরা অনুমান করতে GHCI যথেষ্ট তথ্য দিয়েছেন যে টাইপ a
এখানে Product Integer
, একটি যা instance
এর Monoid
যার <>
অপারেশন পূর্ণসংখ্যা গুণ নয়। সুতরাং pow 2 4
করতে যাও recursively বিস্তৃতি 2<>2<>2<>2
, যা 2*2*2*2
বা 16
। এ পর্যন্ত সব ঠিকই.
তবে আমাদের ফাংশনটিতে কেবল জেনেরিক মনোয়েড অপারেশন ব্যবহার করা হয়। পূর্বে, আমি বললাম আরেক দৃষ্টান্ত নেই Monoid
নামক Sum
, যার <>
অপারেশন +
। আমরা কি তা চেষ্টা করতে পারি?
*StylizedRec> getSum $ pow 2 4
8
*StylizedRec> getSum $ pow 7 2
14
একই বিস্তৃতি এখন আমাদের 2+2+2+2
পরিবর্তে দেয় 2*2*2*2
। বহুগুণ সংযোজন হিসাবে এক্সপেনশনেশন হ'ল গুণ!
তবে আমি হাসকল মোনয়েডের আরও একটি উদাহরণ দিলাম: তালিকাগুলি, যার অপারেশনটি একত্রীকরণ।
*StylizedRec> pow [2] 4
[2,2,2,2]
*StylizedRec> pow [7] 2
[7,7]
লিখন সংকলককে [2]
বলে যে এটি একটি তালিকা, <>
তালিকায় রয়েছে ++
, [2]++[2]++[2]++[2]
তেমন [2,2,2,2]
।
অবশেষে, একটি অ্যালগরিদম (দুই, বাস্তব)
কেবল প্রতিস্থাপন x
সঙ্গে [x]
, আপনি জেনেরিক আলগোরিদিম যে ব্যবহারসমূহ recursion মডিউল একটি semigroup এক যে একটি তালিকা তৈরি করে পরিবর্তিত করে। কোন তালিকা? অ্যালগোরিদম উপাদানগুলির তালিকার জন্য প্রযোজ্য <>
। যেহেতু আমরা কেবলমাত্র সেমিগ্রুপ ক্রিয়াকলাপগুলি ব্যবহার করেছি যা তালিকাগুলি খুব বেশি রয়েছে, ফলে ফলাফলটি মূল গণনার ক্ষেত্রে বিচ্ছিন্ন হবে। এবং যেহেতু মূল অপারেশনটি সহকারী ছিল, তাই আমরা পিছন থেকে সামনের দিক থেকে বা সামনে থেকে উপাদানগুলি সমানভাবে ভালভাবে মূল্যায়ন করতে পারি।
যদি আপনার অ্যালগরিদম কখনও কোনও বেস কেসে পৌঁছায় এবং শেষ হয় তবে তালিকাটি খালি থাকবে না। যেহেতু টার্মিনাল কেসটি কিছু ফিরিয়ে দিয়েছে, এটি তালিকার চূড়ান্ত উপাদান হবে, সুতরাং এতে কমপক্ষে একটি উপাদান থাকবে।
আপনি যাতে তালিকার প্রতিটি উপাদানগুলিতে বাইনারি হ্রাস অপারেশনটি কীভাবে প্রয়োগ করেন? একদম ঠিক আছে। সুতরাং আপনি প্রতিস্থাপন করতে পারেন [x]
জন্য x
দ্বারা কমাতে উপাদানের একটি তালিকা পেতে <>
, এবং তারপর পারেন ডান-ভাঁজ বা তালিকা বাম-ভাঁজ:
*StylizedRec> getProduct $ foldr1 (<>) $ pow [Product 2] 4
16
*StylizedRec> import Data.List
*StylizedRec Data.List> getProduct $ foldl1' (<>) $ pow [Product 2] 4
16
সঙ্গে সংস্করণ foldr1
আসলে মান গ্রন্থাগার বিদ্যমান, যেমন sconcat
জন্য Semigroup
এবং mconcat
জন্য Monoid
। এটি তালিকায় একটি অলস ডান ভাঁজ করে। অর্থাৎ এটি প্রসারিত [Product 2,Product 2,Product 2,Product 2]
হয় 2<>(2<>(2<>(2)))
।
এটি এই ক্ষেত্রে দক্ষ নয় কারণ আপনি সমস্তটি তৈরি না করা পর্যন্ত আপনি স্বতন্ত্র পদগুলির সাথে কিছুই করতে পারবেন না। (এক পর্যায়ে আমার ডান ভাঁজগুলি কখন ব্যবহার করতে হবে এবং কখন কঠোর বাম ভাঁজগুলি ব্যবহার করা উচিত সে সম্পর্কে আমি এখানে আলোচনা করেছি, তবে এটি খুব বেশি দূরে চলে গেছে))
সহ সংস্করণটি foldl1'
কঠোরভাবে মূল্যায়ন করা বাম ভাঁজ। এটি বলতে হয়, একটি কঠোর সঞ্চয়ের সাথে একটি পুচ্ছ-পুনরাবৃত্তি ফাংশন। এটি মূল্যায়ন করে (((2)<>2)<>2)<>2
, তাৎক্ষণিকভাবে গণনা করা হয় এবং পরে যখন প্রয়োজন হয় না। (অন্তত, সেখানে মধ্যে কোন বিলম্ব হয় নিজেই ভাঁজ। তালিকা গুটান হচ্ছে আরেকটি ফাংশন যা অলস মূল্যায়ন থাকতে পারে এখানে উৎপন্ন হয়) সুতরাং, ভাঁজ গণনা করে (4<>2)<>2
, তারপর অবিলম্বে গণনা করে 8<>2
, তারপর 16
। এই কারণেই আমাদের সহযোগী হওয়ার জন্য অপারেশনটি প্রয়োজন: আমরা সবেমাত্র বন্ধনীগুলির গোষ্ঠী পরিবর্তন করেছি!
কঠোর বাম ভাঁজটি জিসিসি যা করছে তার সমতুল্য। পূর্ববর্তী উদাহরণের বামতম সংখ্যাটি হ'ল সঞ্চালক, এক্ষেত্রে একটি চলমান পণ্য। প্রতিটি পদক্ষেপে, এটি তালিকার পরবর্তী সংখ্যা দ্বারা গুণিত হয়। এটি প্রকাশের আর একটি উপায় যা হ'ল: আপনি গুণমানের মানগুলি নিয়ে পুনরাবৃত্তি করেন, চলমান পণ্যটিকে একটি সঞ্চয়ে রেখে রাখেন এবং প্রতিটি পুনরাবৃত্তির উপর, আপনি পরবর্তী মান দ্বারা সংযোজককে গুণ করেন। যে, এটি while
ছদ্মবেশ একটি লুপ।
এটি কখনও কখনও ঠিক দক্ষ হিসাবে তৈরি করা যেতে পারে। সংকলক মেমরিতে তালিকার ডেটা কাঠামোটিকে অপ্টিমাইজ করতে সক্ষম হতে পারে। তত্ত্ব অনুসারে, এখানে এটি করা উচিত তা নির্ধারণের জন্য এটির সংকলনের সময় পর্যাপ্ত তথ্য রয়েছে: [x]
এটি একটি সিঙ্গলটন, [x]<>xs
একইরকম cons x xs
। ফাংশনটির প্রতিটি পুনরাবৃত্তি একই স্ট্যাক ফ্রেমটিকে পুনরায় ব্যবহার করতে এবং প্যারামিটারগুলিকে আপডেট করতে সক্ষম হতে পারে।
একটি ডান ভাঁজ বা একটি কঠোর বাম ভাঁজ একটি উপযুক্ত ক্ষেত্রে আরও উপযুক্ত হতে পারে, তাই আপনি কোনটি চান তা জেনে রাখুন। এমন কিছু জিনিস রয়েছে যা কেবলমাত্র একটি ডান ভাঁজ করতে পারে (যেমন সমস্ত ইনপুটটির জন্য অপেক্ষা না করে ইন্টারেক্টিভ আউটপুট উত্পন্ন করে এবং একটি অসীম তালিকায় কাজ করে)। এখানে, যদিও আমরা অপারেশনগুলির ক্রমকে একটি সাধারণ মানকে হ্রাস করছি, তাই কঠোর বাম ভাঁজটি আমরা চাই।
সুতরাং, যেমন আপনি দেখতে পাচ্ছেন, লেজ-পুনরাবৃত্তি মডুলোর যে কোনও সেমিগ্রুপকে স্বয়ংক্রিয়ভাবে অনুকূলকরণ করা সম্ভব (এর একটি উদাহরণ উদাহরণের মধ্যে যে কোনও সংখ্যাবৃদ্ধির অধীনে রয়েছে) একটি অলস ডান ভাঁজ বা একটি শক্ত বাম ভাঁজ, এক লাইনের এক লাইনে Haskell,।
আরও সাধারণীকরণ
বাইনারি অপারেশনের দুটি আর্গুমেন্ট একই ধরণের হওয়া উচিত নয়, যতক্ষণ না আপনার মান হিসাবে প্রাথমিক মান একই ধরণের হয়। (আপনি অবশ্যই বাম বা ডান যে ধরণের ভাঁজ করছেন তার ক্রমের সাথে মেলে ধরার জন্য আপনি সর্বদা আর্গুমেন্টগুলি ফ্লিপ করতে পারেন)) সুতরাং আপনি বারবার কোনও ফাইল আপডেট করার জন্য একটি প্যাচ যুক্ত করতে পারেন, বা এর প্রাথমিক মান দিয়ে শুরু করতে পারেন 1.0, একটি ভাসমান-পয়েন্ট ফলাফল সংগ্রহ করতে পূর্ণসংখ্যা দ্বারা ভাগ করুন। অথবা একটি তালিকা পেতে খালি তালিকায় উপাদানগুলি প্রিপেন্ড করুন।
আর এক ধরণের সাধারণীকরণ হ'ল ভাঁজগুলি তালিকাগুলিতে নয় বরং অন্যান্য Foldable
ডেটা স্ট্রাকচারে প্রয়োগ করা । প্রায়শই, অপরিবর্তনীয় রৈখিক লিঙ্কযুক্ত তালিকাটি কোনও প্রদত্ত অ্যালগরিদমের জন্য আপনি যে ডেটা কাঠামো চান তা নয়। একটি বিষয় যা আমি উপরে উঠতে পারি নি তা হ'ল পিছনের তুলনায় তালিকার সামনের অংশে উপাদান যুক্ত করা অনেক বেশি দক্ষ এবং যখন অপারেশনটি পরিবর্তনীয় হয় না তখন অপারেশনটির x
বামে এবং ডানদিকে প্রয়োগ হয় না একই. সুতরাং আপনাকে অন্য কাঠামো ব্যবহার করতে হবে, যেমন তালিকার জুড়ি বা বাইনারি গাছের মতো, এমন একটি অ্যালগোরিদম উপস্থাপন করতে x
যা ডানদিকে <>
এবং বামে প্রয়োগ করতে পারে ।
আরও মনে রাখবেন যে সহযোগী সম্পত্তি আপনাকে বিভাজন এবং বিজয়ের মতো অন্যান্য দরকারী উপায়ে ক্রিয়াকলাপগুলি পুনরায় গোষ্ঠীভুক্ত করতে দেয়:
times :: Monoid a => a -> Word -> a
times _ 0 = mempty
times x 1 = x
times x n | even n = y <> y
| otherwise = x <> y <> y
where y = times x (n `quot` 2)
বা স্বয়ংক্রিয় সমান্তরালতা, যেখানে প্রতিটি থ্রেড অন্যের সাথে মিলিত হয়ে এমন কোনও মানকে একটি সাব্রিন্স হ্রাস করে।
if(n==0) return 0;
(আপনার প্রশ্নের মতো 1 টি ফেরেনি )।x^0 = 1
সুতরাং, এটি একটি বাগ। যদিও এটি বাকি প্রশ্নের জন্য গুরুত্বপূর্ণ, তবে; পুনরাবৃত্তির asm প্রথমে সেই বিশেষ ক্ষেত্রে পরীক্ষা করে। তবে আশ্চর্যের বিষয় হল, পুনরাবৃত্তি বাস্তবায়ন উত্সটিতে1 * x
উপস্থিত ছিল না এমন একটি গুণকে পরিচয় করিয়ে দেয়, এমনকি আমরা একটিfloat
সংস্করণ তৈরি করি। gcc.godbolt.org/z/eqwine (এবং জিসিসি কেবল এতেই সফল হয়-ffast-math
))