পূর্ণসংখ্যার জন্য আপনি জাভাতে লগ বেস 2 গণনা করবেন?


138

আমি পূর্ণসংখ্যার জন্য লগ বেস 2 গণনা করতে নিম্নলিখিত ফাংশনটি ব্যবহার করি:

public static int log2(int n){
    if(n <= 0) throw new IllegalArgumentException();
    return 31 - Integer.numberOfLeadingZeros(n);
}

এটির কি সর্বোত্তম পারফরম্যান্স রয়েছে?

কেউ কি সেই লক্ষ্যে প্রস্তুত J2SE এপিআই কার্যকারিতা জানেন?

ইউপিডি 1 আশ্চর্যজনকভাবে আমার কাছে, ফ্লোট পয়েন্ট গাণিতিকগুলি পূর্ণসংখ্যার গাণিতিকের চেয়ে দ্রুত বলে মনে হয়।

ইউপিডি 2 মন্তব্যের কারণে আমি আরও বিশদ তদন্ত করব।

ইউপিডি 3 আমার পূর্ণসংখ্যার পাটিগণিত ফাংশন ম্যাথ.লগ (এন) / ম্যাথ.লগ (2) এর চেয়ে 10 গুণ বেশি দ্রুত।


1
আপনি এর পারফরম্যান্স কিভাবে পরীক্ষা করলেন? আমার সিস্টেমে (কোর i7, jdk 1.6 x64) পূর্ণসংখ্যা সংস্করণটি ভাসমান পয়েন্ট সংস্করণের চেয়ে প্রায় 10 গুণ বেশি দ্রুত। কার্যকারণের ফলাফলের সাথে বাস্তবে এমন কিছু করার বিষয়ে নিশ্চিত হন যাতে জেআইটি পুরোপুরি গণনা সরাতে না পারে!
x4u

আপনি সঠিক. আমি গণনার ফলাফল ব্যবহার করি নি এবং সংকলক কিছু অনুকূল করে নিয়েছে। এখন আপনার মতোই আমারও একই ফলাফল রয়েছে - পূর্ণসংখ্যা ফাংশন 10 গুণ দ্রুত (কোর 2 ডুয়ো, জেডকে 1.6 সি 64)
নলদেবাইস

6
এটি কার্যকরভাবে আপনাকে দেয় Math.floor(Math.log(n)/Math.log(2)), সুতরাং এটি সত্যিই লগ বেস 2 গণনা করছে না!
ডোরি

উত্তর:


74

আপনি যদি পূর্ণসংখ্যার গাণিতিক সাহায্যে ভাসমান-পয়েন্ট ব্যবহার করার কথা ভাবছেন তবে আপনাকে সতর্কতা অবলম্বন করতে হবে।

আমি সাধারণত যখনই সম্ভব এফপি গণনা এড়ানোর চেষ্টা করি।

ভাসমান-পয়েন্ট অপারেশনগুলি সঠিক নয়। আপনি কখনই (int)(Math.log(65536)/Math.log(2))মূল্যায়ন করবেন তা নিশ্চিত করে জানতে পারবেন না । উদাহরণস্বরূপ, Math.ceil(Math.log(1<<29) / Math.log(2))30 আমার পিসিতে যেখানে গাণিতিকভাবে এটা করা উচিত ঠিক 29 আমি x যেখানে জন্য একটি মান খুঁজে পাইনি হয় (int)(Math.log(x)/Math.log(2))ব্যর্থ (ঠিক কারণ সেখানে মাত্র 32 "ডেঞ্জারাস" মান), কিন্তু তার মানে এই নয় যে এটি কাজ করবে যে কোনও পিসিতে একইভাবে।

রাউন্ডিংয়ের সময় এখানে সাধারণ ট্রিকটি "অ্যাপসিলন" ব্যবহার করছে। লাইক (int)(Math.log(x)/Math.log(2)+1e-10)কখনই ব্যর্থ হয় না। এই "এপসিলন" এর পছন্দটি কোনও তুচ্ছ কাজ নয়।

আরও সাধারণ টাস্ক ব্যবহার করে আরও বিক্ষোভ - প্রয়োগের চেষ্টা করা int log(int x, int base):

পরীক্ষার কোড:

static int pow(int base, int power) {
    int result = 1;
    for (int i = 0; i < power; i++)
        result *= base;
    return result;
}

private static void test(int base, int pow) {
    int x = pow(base, pow);
    if (pow != log(x, base))
        System.out.println(String.format("error at %d^%d", base, pow));
    if(pow!=0 && (pow-1) != log(x-1, base))
        System.out.println(String.format("error at %d^%d-1", base, pow));
}

public static void main(String[] args) {
    for (int base = 2; base < 500; base++) {
        int maxPow = (int) (Math.log(Integer.MAX_VALUE) / Math.log(base));
        for (int pow = 0; pow <= maxPow; pow++) {
            test(base, pow);
        }
    }
}

যদি আমরা লগারিদমটির সর্বাধিক সোজা-ফরোয়ার্ড বাস্তবায়ন ব্যবহার করি,

static int log(int x, int base)
{
    return (int) (Math.log(x) / Math.log(base));
}

এই মুদ্রণ:

error at 3^5
error at 3^10
error at 3^13
error at 3^15
error at 3^17
error at 9^5
error at 10^3
error at 10^6
error at 10^9
error at 11^7
error at 12^7
...

ত্রুটিগুলি থেকে সম্পূর্ণরূপে মুক্তি পেতে আমাকে এপসিলন যুক্ত করতে হয়েছিল যা 1e-11 এবং 1e-14 এর মধ্যে রয়েছে। আপনি পরীক্ষা করার আগে এই বলতে পারে? আমি অবশ্যই পারিনি।


3
"এর অর্থ এই নয় যে এটি কোনও পিসিতে একইভাবে কাজ করবে" - আপনি যদি এটি ব্যবহার করেন strictfp, না?
কেন

@ কেন: হতে পারে ... তবে আপনি সমস্ত সম্ভাব্য ইনপুট মানগুলি সম্পূর্ণরূপে পরিগণিত করার পরে নিশ্চিত হতে পারবেন। (আমরা ভাগ্যবান তাদের মধ্যে এখানে খুব কমই
রয়েছি

2
প্রযুক্তিগতভাবে, হ্যাঁ, তবে এটি কোনও ফাংশনের ক্ষেত্রে সত্য। এক পর্যায়ে আপনাকে বিশ্বাস করতে হবে যে আপনি যদি উপলভ্য ডকুমেন্টেশনগুলি ব্যবহার করেন এবং "সমস্ত সম্ভাব্য ইনপুট মান" এর কিছু সুনির্দিষ্ট কিন্তু বর্নিতভাবে ছোট ভগ্নাংশটি পরীক্ষা করেন যে আপনার প্রোগ্রামটি যথেষ্ট ভালভাবে কাজ করবে। strictfpবলে মনে হচ্ছে বাস্তবে এটি কঠোর হওয়ার জন্য প্রচুর বকাবকি করেছে। :-)
কেন

কিভাবে return ((long)Math.log(x) / (long)Math.log(base));সব ত্রুটি সমাধান?
কোনও বাগ নেই

92

এই ফাংশনটি আমি এই গণনার জন্য ব্যবহার করি:

public static int binlog( int bits ) // returns 0 for bits=0
{
    int log = 0;
    if( ( bits & 0xffff0000 ) != 0 ) { bits >>>= 16; log = 16; }
    if( bits >= 256 ) { bits >>>= 8; log += 8; }
    if( bits >= 16  ) { bits >>>= 4; log += 4; }
    if( bits >= 4   ) { bits >>>= 2; log += 2; }
    return log + ( bits >>> 1 );
}

এটি ম্যাথ.লগ () ভিত্তিক প্রয়োগের চেয়ে Integer.numberOfLeadingZeros () (20-30%) এবং প্রায় 10 গুণ দ্রুত (jdk 1.6 x64) এর চেয়ে সামান্য দ্রুত:

private static final double log2div = 1.000000000001 / Math.log( 2 );
public static int log2fp0( int bits )
{
    if( bits == 0 )
        return 0; // or throw exception
    return (int) ( Math.log( bits & 0xffffffffL ) * log2div );
}

উভয় ফাংশন সমস্ত সম্ভাব্য ইনপুট মানগুলির জন্য একই ফলাফল প্রদান করে।

আপডেট: জাভা 1.7 সার্ভার জেআইটি সিপিইউ অন্তর্ভুক্তির ভিত্তিতে বিকল্প বাস্তবায়ন সহ কয়েকটি স্থিতিক গণিত ফাংশন প্রতিস্থাপন করতে সক্ষম। এই ফাংশনগুলির মধ্যে একটি হ'ল Integer.numberOfLeadingZeros ()। সুতরাং একটি 1.7 বা নতুন সার্ভার ভিএম সহ, প্রশ্নের মতো একটি বাস্তবায়ন binlogউপরের তুলনায় আসলে কিছুটা দ্রুত । দুর্ভাগ্যক্রমে ক্লায়েন্ট জেআইটির এই অপ্টিমাইজেশন রয়েছে বলে মনে হয় না।

public static int log2nlz( int bits )
{
    if( bits == 0 )
        return 0; // or throw exception
    return 31 - Integer.numberOfLeadingZeros( bits );
}

এই বাস্তবায়নটি আমি উপরে পোস্ট করা অন্যান্য দুটি বাস্তবায়ন হিসাবে সমস্ত 2 ^ 32 সম্ভাব্য ইনপুট মানগুলির জন্য একই ফলাফল প্রদান করে।

আমার পিসিতে আসল রানটাইমগুলি এখানে রয়েছে (স্যান্ডি ব্রিজ আই 7):

JDK 1.7 32 বিট ক্লায়েন্ট ভিএম:

binlog:         11.5s
log2nlz:        16.5s
log2fp:        118.1s
log(x)/log(2): 165.0s

JDK 1.7 x64 সার্ভার ভিএম:

binlog:          5.8s
log2nlz:         5.1s
log2fp:         89.5s
log(x)/log(2): 108.1s

এটি পরীক্ষার কোড:

int sum = 0, x = 0;
long time = System.nanoTime();
do sum += log2nlz( x ); while( ++x != 0 );
time = System.nanoTime() - time;
System.out.println( "time=" + time / 1000000L / 1000.0 + "s -> " + sum );

9
x86 এর BSRনির্দেশনা দেয় 32 - numberOfLeadingZerosতবে 0 এর জন্য অপরিবর্তিত থাকে, সুতরাং একটি (জেআইটি) সংকলকটি শূন্য-নাহীন জন্য পরীক্ষা করতে হয় যদি এটি প্রমাণ করতে না পারে যে এটি করার দরকার নেই। বিএমআই নির্দেশিকা সেট এক্সটেনশনগুলি (হাসওয়েল এবং আরও নতুন) প্রবর্তন করেছে LZCNT, যা numberOfLeadingZerosএকক নির্দেশে পুরোপুরি বাস্তবায়ন করে । তারা উভয় 3 চক্রের বিলম্ব, প্রতি চক্র থ্রিপুট 1। সুতরাং আমি একেবারে ব্যবহারের পরামর্শ দেব numberOfLeadingZeros, কারণ এটি একটি ভাল জেভিএমের পক্ষে সহজ করে তোলে। (একটি lzcntবিস্ময়কর বিষয় এটি যে এটি বাতিল হয়ে যায় তার নিবন্ধের পুরানো মূল্যের উপর এটি একটি মিথ্যা নির্ভরতা রয়েছে))
পিটার কর্ডেস

আমি জাভা 1.7 সার্ভার জেআইটি সিপিইউ অন্তর্নিহিত প্রতিস্থাপন সম্পর্কে আপনার মন্তব্যে সবচেয়ে আগ্রহী। আপনার কি রেফারেন্স ইউআরএল আছে? (জেআইটি সোর্স কোড
লিঙ্কটিও

37

চেষ্টা Math.log(x) / Math.log(2)


8
গাণিতিকভাবে এটি সঠিক হলেও, দয়া করে সচেতন থাকবেন যে রোটসরের উত্তরে বর্ণিত হিসাবে অদম্য ভাসমান-পয়েন্ট পাটিগণিতের কারণে ভুল গণনার ঝুঁকি রয়েছে।
leeyuiwah

28

আপনি পরিচয় ব্যবহার করতে পারেন

            log[a]x
 log[b]x = ---------
            log[a]b

সুতরাং এটি লগ 2 এর জন্য প্রযোজ্য।

            log[10]x
 log[2]x = ----------
            log[10]2

এটি কেবল জাভা ম্যাথ লগ 10 পদ্ধতিতে প্লাগ করুন ....

http://mathforum.org/library/drmath/view/55565.html


3
গাণিতিকভাবে এটি সঠিক হলেও, দয়া করে সচেতন থাকবেন যে রোটসরের উত্তরে বর্ণিত হিসাবে অদম্য ভাসমান-পয়েন্ট পাটিগণিতের কারণে ভুল গণনার ঝুঁকি রয়েছে।
leeyuiwah

18

কেন না:

public static double log2(int n)
{
    return (Math.log(n) / Math.log(2));
}

6
গাণিতিকভাবে এটি সঠিক হলেও, দয়া করে সচেতন থাকবেন যে রোটসরের উত্তরে বর্ণিত হিসাবে অদম্য ভাসমান-পয়েন্ট পাটিগণিতের কারণে ভুল গণনার ঝুঁকি রয়েছে।
leeyuiwah

9

পেয়ারা লাইব্রেরিতে ফাংশন রয়েছে:

LongMath.log2()

তাই আমি এটি ব্যবহার করার পরামর্শ দিই।


আমি কীভাবে এই অ্যাপ্লিকেশনটিতে এই প্যাকেজটি যুক্ত করতে পারি?
এলভিন মামাডভ

এখান থেকে জারটি ডাউনলোড করুন এবং এটি আপনার প্রকল্পের নির্মাণের পথে যুক্ত করুন।
ডিবোস্মিত রায়

2
আমি কি কেবল একটি ফাংশন ব্যবহার করতে আমার অ্যাপ্লিকেশনটিতে একটি গ্রন্থাগার যুক্ত করব?
তাশ পেমিভা

7
আপনি কেন এটি ব্যবহার করার পরামর্শ দিচ্ছেন? পেয়ারা উত্সের দ্রুত পঠন থেকে বোঝা যায় যে এটি অপারেশন নির্ভরতা যুক্ত করার ব্যয়ে ওপির পদ্ধতিতে (কোডের কয়েকটি খুব স্পষ্টভাবে বোঝা লাইন) হিসাবে একই কাজ করে। গুগল কিছু সরবরাহ করে বলেই সমস্যাটি বোঝার চেয়ে নিজেকে সমাধান করার চেয়ে কোনও উন্নতি করতে পারে না।
ডেভ

3

X4u উত্তর যুক্ত করতে, যা আপনাকে একটি সংখ্যার বাইনারি লগের মেঝে দেয়, এই ফাংশনটি একটি সংখ্যার বাইনারি লগের সিলটি ফিরিয়ে দেয়:

public static int ceilbinlog(int number) // returns 0 for bits=0
{
    int log = 0;
    int bits = number;
    if ((bits & 0xffff0000) != 0) {
        bits >>>= 16;
        log = 16;
    }
    if (bits >= 256) {
        bits >>>= 8;
        log += 8;
    }
    if (bits >= 16) {
        bits >>>= 4;
        log += 4;
    }
    if (bits >= 4) {
        bits >>>= 2;
        log += 2;
    }
    if (1 << log < number)
        log++;
    return log + (bits >>> 1);
}

"সংখ্যা" পরিবর্তনশীল কোথায়?
বারটেক

3

আমি ম্যাথ.লগ 10 ব্যবহার করার সময় কিছু ক্ষেত্রে সবেমাত্র কাজ করা হয়েছিল:

public static double log2(int n)
{
    return (Math.log10(n) / Math.log10(2));
}

0

আসুন যোগ করুন:

int[] fastLogs;

private void populateFastLogs(int length) {
    fastLogs = new int[length + 1];
    int counter = 0;
    int log = 0;
    int num = 1;
    fastLogs[0] = 0;
    for (int i = 1; i < fastLogs.length; i++) {
        counter++;
        fastLogs[i] = log;
        if (counter == num) {
            log++;
            num *= 2;
            counter = 0;
        }
    }
}

সূত্র: https://github.com/pochuan/cs166/blob/master/ps1/rmq/SparseTableRMQ.java


এটি একটি অনুসন্ধান সারণী তৈরি করা হবে। ওপি লগারিদমকে "গণনা" করার জন্য দ্রুততর উপায়ের অনুরোধ করেছিল।
ডেভ

-4

এন এর লগ বেস 2 গণনা করতে, নিম্নলিখিত এক্সপ্রেশন ব্যবহার করা যেতে পারে:

double res = log10(n)/log10(2);

2
এই উত্তর ইতিমধ্যে বেশ কয়েকবার পোস্ট করা হয়েছে, এবং ইতিমধ্যে রাউন্ড-অফ ত্রুটির কারণে সম্ভাব্যভাবে ভুল বলে চিহ্নিত হয়েছে। অবিচ্ছেদ্য মান জিজ্ঞাসা করা ওপি নোট করুন; এখান থেকে পূর্ণসংখ্যার দিকে যাওয়ার জন্য গোলাকার নির্ভুলতাটি কী প্রয়োজন তা মোটেও পরিষ্কার নয়।
21
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.