سؤال

لدي مجموعة من أرقام النقطة العائمة (جافا الزوجي)، معظمها قريب جدا من 1، وأحتاج إلى ضربها معا كجزء من حساب أكبر. ولست بحاجة للقيام بذلك كثير.

المشكلة هي أنه في حين أن جافا يتضاعف ليس لديك مشكلة مع عدد مثل:

0.0000000000000000000000000000000001 (1.0E-34)

لا يمكنهم تمثيل شيء مثل:

1.0000000000000000000000000000000001

وبالتالي، فقد فقدت الدقة بسرعة (يبدو أن الحد يبلغ حوالي 1.000000000000001 لوجود جافا).

لقد كنت تعتبر فقط تخزين الأرقام التي يتم طرحها مع 1، لذلك على سبيل المثال 1.0001 سيتم تخزينها على أنها 0.0001 - ولكن المشكلة هي أن تضاعفها معا مرة أخرى لا بد لي من إضافة 1 وفي هذه المرحلة أفقد الدقة.

لمعالجة هذا، يمكنني استخدام BigDecimals لإجراء الحساب (تحويل إلى BigDeCimal، أضف 1.0، ثم اعتاد مرة أخرى إلى الزوجي بعد ذلك، لكن لدي مخاوف جدية بشأن آثار أداء هذا الأمر.

هل يمكن لأي شخص أن يرى طريقة للقيام بذلك يتجنب استخدام bigdecimal؟

تحرير للوضوح: هذا للحصول على مرشح تعاوني واسع النطاق، والذي يستخدم خوارزمية تحسين نزول التدرج. دقة هي مشكلة لأن المرشح التعاوني غالبا ما يتعامل مع أرقام صغيرة جدا (مثل احتمال وجود شخص ينقر على إعلان للمنتج، مما قد يكون 1 في 1000 أو 1 في 10000).

السرعة هي مشكلة لأن المرشح التعاوني يجب تدريبه على عشرات الملايين من نقاط البيانات، إن لم يكن أكثر.

هل كانت مفيدة؟

المحلول

نعم: ل

(1 + x) * (1 + y) = 1 + x + y + x*y

في حالتك، x و y صغيرة جدا، لذلك x*y سوف يكون بعيد أصغر - طريق صغير جدا للتأثير على نتائج حسابك. حتى بقدر ما تشعر بالقلق،

(1 + x) * (1 + y) = 1 + x + y

هذا يعني أنه يمكنك تخزين الأرقام مع 1 طرحها، وبدلا من الضرب، أضفها فقط. طالما أن النتائج هي دائما أقل بكثير من 1، فستكونون قريبا بما يكفي للنتائج الدقيقة للرياضيات التي لن تهتم بالفرق.

تعديل: فقط لاحظت: أنت تقول عظم منهم قريبون جدا من 1. من الواضح أن هذه التقنية لن تعمل للأرقام التي ليست قريبة من 1 - وهذا هو، إذا x و y كبيرة. ولكن إذا كان أحدهما كبيرا وصغير واحد، فقد لا يزال يعمل؛ أنت تهتم فقط بحجم المنتج x*y. وبعد (وإذا لم تكن كلا الأرقامين قريبة من 1، فيمكنك فقط استخدام جافا العادية double عمليه الضرب...)

نصائح أخرى

ربما يمكنك استخدام اللوغاريتمي؟

logaritms تقليل الضرب بسهولة للإضافة.

أيضا، لرعاية الخسارة الدقيقة الأولية، هناك LOG1P وظيفة (على الأقل، موجود في C / C ++)، والتي ترجع السجل (1 + x) دون أي خسارة دقيقة. (مثل LOG1P (1E-30) إرجاع 1E-30 بالنسبة لي)

ثم يمكنك استخدام Expm1 للحصول على الجزء العشري من النتيجة الفعلية.

ليس هذا النوع من الوضع بالضبط ما هو bigdecimal؟

تم تحريره لإضافة:

"في الفقرة الثانية الثانية، أفضل تجنب bigdecimals إذا كان ذلك ممكنا لأسباب الأداء". - الصحة العقلية

"الأمثل المبكر هو جذر كل الشرور" - كيلوث

هناك حل بسيط مصنوع عمليا لطلب مشكلتك. أنت قلق على أنه قد لا يكون سريعا بما يكفي، لذلك تريد أن تفعل شيئا معقدا فكر في سيكون أسرع. ينفث اقتباس الرنوث في بعض الأحيان، لكن هذا هو بالضبط الوضع الذي كان يحذره. اكتبها بطريقة بسيطة. قم بتجريبه. الملف الشخصي عليه. معرفة ما إذا كان بطيئا جدا. اذا كانت ومن بعد البدء في التفكير في طرق لجعلها أسرع. لا تضيف كل هذا التعليمات البرمجية المعقدة الإضافية، حتى تعرف أنه من الضروري.

اعتمادا على حيث تأتي الأرقام من وكيف تستخدمها، قد ترغب في استخدام العقلانية بدلا من العوامات. ليس الجواب الصحيح لجميع الحالات، ولكن عندما يكون يكون الجواب الصحيح ليس هناك حقا آخر.

إذا لم تناسب العقلانية، فسوف أؤيد إجابة اللوغاريتمي.

تحرير استجابة لتعديلك:

إذا كنت تتعامل مع الأرقام التي تمثل معدلات منخفضة الاستجابة، فما تفعل ما يفعله العلماء:

  • تمثلهم كشركة فائضة / العجز (تطبيع الجزء 1.0)
  • مقياس لهم. فكر فيما يتعلق ب "أجزاء لكل مليون" أو كل ما هو مناسب.

هذا سوف يتركك التعامل مع أرقام معقولة للحسابات.

تجدر الإشارة إلى أنك تقوم باختبار حدود أجهزةك بدلا من جافا. يستخدم Java النقطة العائمة 64 بت في وحدة المعالجة المركزية الخاصة بك.

أقترح عليك اختبار أداء التصوير التوقعات قبل أن تفترض أنه لن يكون سريعا بما يكفي بالنسبة لك. لا يزال بإمكانك القيام بعشرات الآلاف من الحسابات في الثانية الواحدة مع BigDeCimal.

كما يشير ديفيد، يمكنك فقط إضافة الإزاحة.

(1 + x) * (1 + y) = 1 + x + y + x * y

ومع ذلك، يبدو الأمر محفوفا بالمخاطر لاختيار إخراج المدى الأخير. لا. على سبيل المثال، جرب هذا:

X = 1E-8 Y = 2E-6 Z = 3E-7 W = 4E-5

ما هو (1 + x)(1 + ص)(1 + z) * (1 + W)؟ في دقة مزدوجة، أحصل على:

(1 + x)(1 + ص)(1 + z) * (1 + W)

ans =

      1.00004231009302

ومع ذلك، نرى ما يحدث إذا كنا نقوم بالتقريب المضافة البسيط.

1 + (x + y + z + w)

ans =

            1.00004231

فقدنا البتات المنخفضة التي قد تكون مهمة. هذه مشكلة فقط إذا كانت بعض الاختلافات من 1 في المنتج هي SQRT على الأقل (EPS)، حيث EPS هي الدقة التي تعمل فيها.

جرب هذا بدلا من ذلك:

f = @ (u، v) u + v + u * v؛

النتيجة = f (x، y)؛

النتيجة = f (النتيجة، z)؛

النتيجة = f (النتيجة، W)؛

1 + النتيجة

ans =

      1.00004231009302

كما ترون، هذا يعيدنا إلى نتيجة الدقة المزدوجة. في الواقع، إنه أكثر دقة بعض الشيء، لأن القيمة الداخلية للنتيجة هي 4.23100930230249E-05.

إذا كنت بحاجة فعلا إلى الدقة، فسيتعين عليك استخدام شيء مثل التصويري، حتى لو كان أبطأ من المزدوج.

إذا كنت لا تحتاج حقا الدقة، فيمكنك الذهاب مع إجابة ديفيد. ولكن حتى إذا كنت تستخدم الصدد كثيرا، فقد يكون بعض التحسين المبكر، لذلك قد يكون التصميم الكبير هو الطريق للذهاب على أي حال

عندما تقول "معظمها قريب جدا من 1"، كم، بالضبط؟

ربما يمكن أن يكون لديك إزاحة ضمنية من 1 في جميع أرقامك وتعمل فقط مع الكسور.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top