পা * (ক, ডি, এন) ** ডি% এন এর চেয়ে এত দ্রুত কেন?


110

আমি মিলার-রবিন আদিমতার পরীক্ষাটি বাস্তবায়নের চেষ্টা করছিলাম , এবং বিস্মিত হয়েছিলাম যে এটি কেন মাঝারি আকারের সংখ্যা (~ 7 ডিজিট) জন্য এত দীর্ঘ (> 20 সেকেন্ড) সময় নিচ্ছে? অবশেষে আমি নীচের কোডের লাইনটি সমস্যার উত্স হিসাবে পেয়েছি:

x = a**d % n

(যেখানে a, dএবং nসবগুলি সমান, তবে অসম, মিডসাইজ সংখ্যা **হ'ল এক্সপেনসেন্টেশন অপারেটর এবং %এটি মডুলো অপারেটর)

আমি তখন নিম্নলিখিতটি দিয়ে এটি প্রতিস্থাপনের চেষ্টা করেছি:

x = pow(a, d, n)

এবং এটি তুলনা করে এটি প্রায় তাত্ক্ষণিক।

প্রসঙ্গে, এখানে মূল ফাংশনটি রয়েছে:

from random import randint

def primalityTest(n, k):
    if n < 2:
        return False
    if n % 2 == 0:
        return False
    s = 0
    d = n - 1
    while d % 2 == 0:
        s += 1
        d >>= 1
    for i in range(k):
        rand = randint(2, n - 2)
        x = rand**d % n         # offending line
        if x == 1 or x == n - 1:
            continue
        for r in range(s):
            toReturn = True
            x = pow(x, 2, n)
            if x == 1:
                return False
            if x == n - 1:
                toReturn = False
                break
        if toReturn:
            return False
    return True

print(primalityTest(2700643,1))

সময় নিরূপণের একটি উদাহরণ:

from timeit import timeit

a = 2505626
d = 1520321
n = 2700643

def testA():
    print(a**d % n)

def testB():
    print(pow(a, d, n))

print("time: %(time)fs" % {"time":timeit("testA()", setup="from __main__ import testA", number=1)})
print("time: %(time)fs" % {"time":timeit("testB()", setup="from __main__ import testB", number=1)})

আউটপুট (পাইপাই 1.9.0 দিয়ে চালানো):

2642565
time: 23.785543s
2642565
time: 0.000030s

আউটপুট (পাইথন ৩.৩.০ দিয়ে চালানো, ২.7.২ খুব একই সময়ে ফিরে আসে):

2642565
time: 14.426975s
2642565
time: 0.000021s

এবং একটি সম্পর্কিত প্রশ্ন, পাইথন 2 বা 3 পিপাইয়ের সাথে চালানোর সময় এই গণনাটি কেন দ্বিগুণ দ্রুত হয়, যখন সাধারণত পাইপি আরও দ্রুত হয় ?

উত্তর:


164

মডুলার ক্ষয়ক্ষতির উপর উইকিপিডিয়া নিবন্ধটি দেখুন । মূলত, আপনি যখন করবেন a**d % n, আপনাকে আসলে গণনা a**dকরতে হবে, যা বেশ বড় হতে পারে। তবে নিজেই গননা a**d % nনা করে কম্পিউটিংয়ের বিভিন্ন উপায় রয়েছে a**dএবং এটিই powঘটে। **অপারেটর এটা করতে পারব না, কারণ এটা নয় "ভবিষ্যতে মধ্যে দেখুন" করতে পারেন জানাতে চাই যে, আপনি অবিলম্বে মডুলাস নিতে যাচ্ছি।


14
+1 এটাই প্রকৃতপক্ষে ডাস্ট্রিংয়ের দ্বারা বোঝানো হয়েছে>>> print pow.__doc__ pow(x, y[, z]) -> number With two arguments, equivalent to x**y. With three arguments, equivalent to (x**y) % z, but may be more efficient (e.g. for longs).
হেডে ভ্যান ডের হাইড

6
আপনার পাইথন সংস্করণের উপর নির্ভর করে এটি কেবলমাত্র কিছু শর্তের মধ্যে সত্য হতে পারে। আইআইআরসি, ৩.x এবং ২.7 এ, আপনি কেবলমাত্র তিনটি যুক্তিযুক্ত ফর্মটি অবিচ্ছেদ্য ধরণের (এবং অ-নেতিবাচক শক্তি) দিয়ে ব্যবহার করতে পারেন এবং আপনি সর্বদা দেশীয় intটাইপের সাথে মডুলার এক্সপেনসিয়েনশন পাবেন তবে অগত্যা অন্যান্য অবিচ্ছেদ্য প্রকারের সাথে নয়। তবে পুরানো সংস্করণগুলিতে সি-তে ফিট করার নিয়ম ছিল long, ত্রি-যুক্তির ফর্ম floatইত্যাদির জন্য অনুমতি দেওয়া হয়েছিল ইত্যাদি। (আশা করি আপনি ২.১ বা তার আগের ব্যবহার করছেন না এবং সি মডিউলগুলি থেকে কোনও কাস্টম ইন্টিগ্রাল টাইপ ব্যবহার করছেন না, তাই কিছুই নেই এই আপনি এর কাছে গুরুত্বপূর্ণ এর)।
abarnert

13
আপনার উত্তর থেকে দেখে মনে হচ্ছে কোনও সংকলকের পক্ষে অভিব্যক্তিটি দেখা এবং এটি অনুকূল করা অসম্ভব, যা সত্য নয়। এটি কেবল ঘটে যায় যে কোনও পাইথন সংকলক এটি করে না।
danielkza

5
@ ড্যানিয়েলকজা: এটি সত্য, তাত্ত্বিকভাবে এটি অসম্ভব বলে বোঝানোর অর্থ আমার ছিল না। "ভবিষ্যতের দিকে নজর দিতে পারে না" এর চেয়ে সম্ভবত "ভবিষ্যতের দিকে তাকাবে না" "আরও সঠিক হবে। তবে দ্রষ্টব্য, অপ্টিমাইজেশন সাধারণভাবে অত্যন্ত কঠিন বা এমনকি অসম্ভব হতে পারে। জন্য ধ্রুবক operands এটা অপ্টিমাইজ পারে, কিন্তু এর মধ্যে x ** y % n, xএকটি বস্তুর হতে পারে যে কার্যকরী __pow__এবং, একটি র্যান্ডম সংখ্যার উপর ভিত্তি করে, বিভিন্ন বাস্তবায়ন বস্তুর এক ফেরৎ __mod__উপায়ে যে র্যান্ডম সংখ্যা উপর নির্ভর করে, ইত্যাদি
BrenBarn

2
@ ডানিয়েলকজা: এছাড়াও, ফাংশনগুলির একই ডোমেন নেই: .3 ** .4 % .5পুরোপুরি আইনী, তবে সংকলক যদি এটি রূপান্তরিত করে তবে এটি pow(.3, .4, .5)উত্থাপন করবে TypeError। কম্পাইলার জানি পাবে হবে a, dএবং n(হয়তো বা শুধু ধরনের বিশেষভাবে অবিচ্ছেদ্য টাইপ মান হতে নিশ্চিত করা হয় int, কারণ রূপান্তর অন্যথায় সাহায্য না), এবং dঅ নেতিবাচক হতে নিশ্চিত করা হয়। এটি একটি জেআইটি অনুমিতভাবে করতে পারে এমন কিছু, তবে গতিশীল ধরণের ভাষার জন্য একটি স্ট্যাটিক সংকলক এবং কোনও অনুমান কেবল তা করতে পারে না।
abarnert

37

ব্রেনবার্ন আপনার মূল প্রশ্নের উত্তর দিয়েছে। আপনার পক্ষে:

পাইপাইয়ের তুলনায় পাইথন 2 বা 3 দিয়ে যখন চালানো হয় তখন প্রায় দ্বিগুণ দ্রুত কেন হয়, যখন সাধারণত পাইপি আরও দ্রুত হয়?

আপনি যদি পাইপির পারফরম্যান্স পৃষ্ঠাটি পড়েন তবে পাইপাই ঠিক তেমনই জিনিস যা পাইপাই ভাল নয় — বাস্তবে, তারা তাদের প্রথম উদাহরণ দেয়:

খারাপ উদাহরণগুলির মধ্যে বৃহত্তর দীর্ঘায়িতদের সাথে গণনা করা অন্তর্ভুক্ত - যা অপ্রয়োজনীয় সমর্থন কোড দ্বারা সম্পাদিত হয়।

তাত্ত্বিকভাবে, একটি বিশাল ক্ষতিকারক একটি মডেলকে মডিউলার কাণ্ডে পরিণত করা (কমপক্ষে প্রথম পাসের পরে) এমন একটি রূপান্তর যা একটি জেআইটি বানাতে সক্ষম হতে পারে… তবে পাইপির জেআইটি নয়।

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


2
দীর্ঘস্থায়ী স্থির হয়েছে। পাইপি 2.0 বিটা 1 ব্যবহার করে দেখুন (এটি সিপিথনের চেয়ে দ্রুত হবে না তবে ধীরে ধীরে হওয়া উচিত নয়)। জিএমপির মেমোরি ইরার হ্যান্ডেল করার উপায় নেই :(
ফিজাল

@ ফিজাল: হ্যাঁ, এবং gmpyকয়েকটি ক্ষেত্রে দ্রুতগতির পরিবর্তে ধীরগতির হয় এবং অনেকগুলি সহজ জিনিসকে কম সুবিধাজনক করে তোলে। এটি সর্বদা উত্তর নয় - তবে কখনও কখনও এটি হয়। সুতরাং আপনি যদি বিশাল সংখ্যার সাথে কাজ করছেন এবং পাইথনের নেটিভ ধরণটি যথেষ্ট দ্রুত বলে মনে হচ্ছে না তা দেখার মতো worth
অবতারিত

1
এবং যদি আপনার যত্ন না থাকে যে আপনার সংখ্যাগুলি বড়
হওয়ায়

1
এটি সেই ফ্যাক্টরটি যা পাইপাই তার দীর্ঘকালীনতার জন্য জিএমপি লাইব্রেরি ব্যবহার না করে। এটি আপনার পক্ষে ঠিক আছে, পাইথন ভিএম বিকাশকারীদের পক্ষে এটি ঠিক নয়। মেলোক প্রচুর র‍্যাম ব্যবহার না করে ব্যর্থ হতে পারে, কেবল সেখানে খুব বড় সংখ্যক রেখে দিন। সেই দিক থেকে GMP এর আচরণ অপরিজ্ঞাত এবং পাইথন এটির অনুমতি দিতে পারে না।
ফিজাল

1
@ ফিজাল: আমি সম্পূর্ণরূপে সম্মত হই যে পাইথনের অন্তর্নির্মিত ধরণের প্রয়োগের জন্য এটি ব্যবহার করা উচিত নয়। এর অর্থ এই নয় যে এটি কখনও কোনও কিছুর জন্য ব্যবহার করা উচিত নয়।
7:30

11

সেখানে মডুলার exponentiation করছেন শর্টকাট আছে: উদাহরণস্বরূপ, আপনি জানতে পারেন a**(2i) mod nযে জন্য iথেকে 1থেকে log(d)এবং সংখ্যাবৃদ্ধি একসঙ্গে (গেলিক ভাষার nঅন্তর্বর্তী ফলাফল আপনার যা দরকার)। 3-টি যুক্তির মতো একটি উত্সর্গীকৃত মডুলার-এক্সপেনসিয়েশন ফাংশন এ pow()জাতীয় কৌশলগুলি উপকার করতে পারে কারণ এটি জানেন যে আপনি মডুলার পাটিগণিত করছেন। পাইথন পার্সার খালি প্রকাশের a**d % nফলে এটি সনাক্ত করতে পারে না , সুতরাং এটি সম্পূর্ণ গণনা সম্পাদন করবে (যা আরও বেশি সময় নিবে)।


3

পথ x = a**d % nগণনা করা হয় বাড়াতে হয় aকরার dক্ষমতা, তারপর যে modulo n। প্রথমত, যদি aবড় হয়, এটি একটি বিশাল সংখ্যা তৈরি করে যা পরে কাটা হয়। তবে, x = pow(a, d, n)সম্ভবত বেশিরভাগই অনুকূলিত করা হয়েছে যাতে কেবলমাত্র শেষ nসংখ্যাগুলি ট্র্যাক করা হয়, যা কোনও সংখ্যার গুণনীয়ক গণনা করার জন্য প্রয়োজনীয়।


6
"x ** d গণনা করার জন্য এটির d গুণ করা দরকার" - সঠিক নয়। আপনি এটি ও (লগ ডি) (খুব প্রশস্ত) গুনে করতে পারেন। স্কোয়ারিং দ্বারা এক্সপেনসিটিশন কোনও মডিউল ছাড়াই ব্যবহার করা যেতে পারে। গুণকগুলির নিখুঁত আকারটি এখানে নেতৃত্ব দেয়।
জন ডিভোরাক

@JanDvorak এটা ঠিক যে, আমি নিশ্চিত কেন আমি ভেবেছিলাম পাইথন জন্য একই exponentiation অ্যালগরিদম ব্যবহার করবে না নই **জন্য pow
ইউশি

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