亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

搜索
首頁 > Java > java教程 > 正文

使用BigInteger和備忘錄模式高效判斷階乘首位數(shù)字奇偶性

聖光之護
發(fā)布: 2025-10-16 14:43:00
原創(chuàng)
361人瀏覽過

使用BigInteger和備忘錄模式高效判斷階乘首位數(shù)字奇偶性

本文探討了如何在給定范圍內(nèi)查找階乘首位為偶數(shù)的數(shù)字。針對傳統(tǒng)數(shù)據(jù)類型在計算大數(shù)階乘時可能遇到的溢出問題,文章詳細介紹了如何利用java的`biginteger`處理任意大小的階乘結(jié)果,并結(jié)合備忘錄模式(memoization)優(yōu)化計算過程,避免重復(fù)計算,從而實現(xiàn)高效且準確的解決方案。

問題描述

我們的目標是在一個指定的整數(shù)范圍 [a, b] 內(nèi),找出所有滿足其階乘結(jié)果的首位數(shù)字為偶數(shù)的整數(shù)。例如,在 [1, 10] 的范圍內(nèi),2! = 2 (首位為偶數(shù)), 3! = 6 (首位為偶數(shù)), 4! = 24 (首位為偶數(shù)), 8! = 40320 (首位為偶數(shù))。因此,該范圍內(nèi)的結(jié)果應(yīng)為 [2, 3, 4, 8]。

常見陷阱:long 類型溢出

在處理階乘計算時,一個常見的陷阱是使用標準整數(shù)類型(如 int 或 long)來存儲結(jié)果。階乘函數(shù)增長非常迅速:

  • 13! = 6,227,020,800
  • 20! = 2,432,902,008,176,640,000 Java的 long 類型最大值約為 9 * 10^18。這意味著,當(dāng)計算 20! 時,long 類型就會發(fā)生溢出,導(dǎo)致結(jié)果不正確。

考慮以下原始代碼片段:

List<Integer> process(int a, int b) {
    long base = i; // 這里的i應(yīng)該初始化為1,并計算a!
    for(int i=1; i<=a; i++) base *= i; // 假設(shè)這里是計算a!

    if(even(base)) list.add(a); // 這里的even(base)在溢出后會給出錯誤結(jié)果

    for(int i=a+1; i<=b; i++) {
        base *= i; // 從a+1開始,base繼續(xù)乘以i
        if(even(base)) list.add(i);
    }
    return list;
}

boolean even(long k) {
    // 將long轉(zhuǎn)換為字符串,然后檢查首位
    int z = ("" + k).charAt(0) - '0';
    return z % 2 == 0;
}
登錄后復(fù)制

這段代碼在 a 或 b 較大時會失敗,因為 long 類型的 base 會在 20! 左右溢出。一旦溢出,base 的值將不再代表真實的階乘結(jié)果,后續(xù)通過 ("" + k).charAt(0) 獲取的首位數(shù)字也將是錯誤的。這就是為什么編碼挑戰(zhàn)中會有隱藏測試用例失敗的原因。

解決方案核心:BigInteger 的應(yīng)用

為了解決 long 類型溢出的問題,我們需要使用能夠處理任意精度整數(shù)的類型。在 Java 中,java.math.BigInteger 類正是為此目的設(shè)計的。BigInteger 對象可以存儲任意大小的整數(shù),從而確保階乘計算的準確性,無論數(shù)字有多大。

使用 BigInteger 進行階乘計算的基本思路是:

無階未來模型擂臺/AI 應(yīng)用平臺
無階未來模型擂臺/AI 應(yīng)用平臺

無階未來模型擂臺/AI 應(yīng)用平臺,一站式模型+應(yīng)用平臺

無階未來模型擂臺/AI 應(yīng)用平臺35
查看詳情 無階未來模型擂臺/AI 應(yīng)用平臺
  1. 初始化一個 BigInteger 對象為 1。
  2. 在循環(huán)中,將當(dāng)前的 BigInteger 結(jié)果與下一個整數(shù)(作為 BigInteger 對象)相乘。

性能優(yōu)化:備忘錄模式(Memoization)

在給定范圍 [a, b] 內(nèi)查找數(shù)字時,我們可能需要計算多個連續(xù)的階乘(例如 5!、6!、7! 等)。每次都從頭開始計算階乘效率較低。備忘錄模式(或動態(tài)規(guī)劃的自底向上方法)可以顯著提高效率。

其思想是:

  1. 存儲已經(jīng)計算過的階乘結(jié)果及其首位數(shù)字的奇偶性。
  2. 當(dāng)需要計算 n! 時,首先檢查是否已經(jīng)計算過。
  3. 如果已經(jīng)計算過,直接返回存儲的結(jié)果。
  4. 如果未計算過,則從上一個已計算的階乘 (n-1)! 開始,逐步計算到 n!,并將每個中間結(jié)果及其首位數(shù)字的奇偶性存儲起來,以供將來使用。

實現(xiàn)細節(jié)與代碼示例

我們將創(chuàng)建一個 Fact 記錄(或一個簡單的類)來存儲每個數(shù)字 n 對應(yīng)的 BigInteger 階乘值和其首位數(shù)字的奇偶性。

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

public class FactorialFirstDigitParity {

    // Fact 記錄用于存儲階乘結(jié)果、對應(yīng)的數(shù)字n以及首位數(shù)字的奇偶性
    // parity為true表示首位為偶數(shù),false表示首位為奇數(shù)
    record Fact(BigInteger fact, int n, boolean parity) {}

    // 靜態(tài)列表用于存儲已計算的階乘結(jié)果,實現(xiàn)備忘錄模式
    // 初始化時包含0!, 1!, 2! 的結(jié)果
    static List<Fact> computed = new ArrayList<>(List.of(
            new Fact(BigInteger.ONE, 0, false), // 0! = 1 (首位為奇數(shù))
            new Fact(BigInteger.ONE, 1, false), // 1! = 1 (首位為奇數(shù))
            new Fact(BigInteger.TWO, 2, true)   // 2! = 2 (首位為偶數(shù))
    ));

    /**
     * 判斷給定數(shù)字 n 的階乘首位是否為偶數(shù)。
     * 使用備忘錄模式優(yōu)化。
     * @param n 要計算階乘的數(shù)字
     * @return 如果 n! 的首位為偶數(shù),則返回 true;否則返回 false。
     */
    public static boolean factorialStartsWithEven(int n) {
        // 如果 n 小于等于當(dāng)前已計算的最大階乘數(shù),直接從備忘錄中獲取結(jié)果
        if (n < computed.size()) {
            return computed.get(n).parity;
        }

        // 獲取備忘錄中最后一個已計算的階乘結(jié)果
        Fact lastComputedFact = computed.get(computed.size() - 1);
        BigInteger currentFactValue = lastComputedFact.fact; // 從上一個階乘值開始
        Fact result = null;

        // 從上一個已計算的數(shù)字 n+1 開始,逐步計算到目標 n
        for (int k = lastComputedFact.n + 1; k <= n; k++) {
            // 計算新的階乘值: (k-1)! * k
            currentFactValue = currentFactValue.multiply(BigInteger.valueOf(k));

            // 獲取當(dāng)前階乘值的字符串表示,并提取首位數(shù)字
            char firstChar = currentFactValue.toString().charAt(0);
            int firstDigit = Character.digit(firstChar, 10); // 將字符轉(zhuǎn)換為數(shù)字

            // 判斷首位數(shù)字的奇偶性
            boolean isEvenParity = (firstDigit % 2 == 0);

            // 創(chuàng)建新的 Fact 記錄并添加到備忘錄中
            result = new Fact(currentFactValue, k, isEvenParity);
            computed.add(result);
        }
        // 返回目標 n 的階乘首位奇偶性
        return result.parity;
    }

    /**
     * 在指定范圍內(nèi)查找所有階乘首位為偶數(shù)的數(shù)字。
     * @param start 范圍的起始值
     * @param end 范圍的結(jié)束值
     * @return 包含所有符合條件的數(shù)字的列表
     */
    public static List<Integer> getForRange(int start, int end) {
        List<Integer> results = new ArrayList<>();
        for (int i = start; i <= end; i++) {
            if (factorialStartsWithEven(i)) {
                results.add(i);
            }
        }
        return results;
    }

    public static void main(String[] args) {
        // 示例用法:查找 1 到 20 之間階乘首位為偶數(shù)的數(shù)字
        List<Integer> list = getForRange(1, 20);
        System.out.println("在 [1, 20] 范圍內(nèi),階乘首位為偶數(shù)的數(shù)字有: " + list); 
        // 預(yù)期輸出: [2, 3, 4, 8, 12, 13, 14, 16, 18, 20]

        // 示例用法:查找 1 到 10 之間階乘首位為偶數(shù)的數(shù)字
        List<Integer> listSmallRange = getForRange(1, 10);
        System.out.println("在 [1, 10] 范圍內(nèi),階乘首位為偶數(shù)的數(shù)字有: " + listSmallRange);
        // 預(yù)期輸出: [2, 3, 4, 8]
    }
}
登錄后復(fù)制

代碼說明:

  1. Fact 記錄: 一個簡潔的數(shù)據(jù)結(jié)構(gòu),用于封裝階乘值 (BigInteger fact)、對應(yīng)的數(shù)字 (int n) 和其首位數(shù)字的奇偶性 (boolean parity)。
  2. computed 列表: 這是一個靜態(tài)的 ArrayList,作為我們的備忘錄。它預(yù)先存儲了 0!、1! 和 2! 的結(jié)果,以便后續(xù)計算可以從已知點開始。
  3. factorialStartsWithEven(int n) 方法:
    • 首先檢查 n 是否已經(jīng)在 computed 列表中。如果是,直接返回已存儲的奇偶性,這是備忘錄模式的關(guān)鍵。
    • 如果 n 尚未計算,它會從 computed 列表中最后一個已知的階乘值 (lastComputedFact) 開始,逐步計算到 n!。
    • 在每次迭代中,currentFactValue 會乘以 k,得到新的階乘值。
    • currentFactValue.toString().charAt(0) 用于獲取 BigInteger 結(jié)果的首位字符。
    • Character.digit(firstChar, 10) 將字符轉(zhuǎn)換為對應(yīng)的整數(shù)數(shù)字。
    • firstDigit % 2 == 0 判斷首位數(shù)字的奇偶性。
    • 每個計算出的階乘結(jié)果(Fact 對象)都會被添加到 computed 列表中,以便后續(xù)查詢。
  4. getForRange(int start, int end) 方法: 遍歷指定范圍 [start, end] 中的每個數(shù)字,調(diào)用 factorialStartsWithEven 方法,并將所有符合條件的數(shù)字收集到一個列表中返回。
  5. main 方法: 提供了示例用法,展示了如何調(diào)用 getForRange 方法并打印結(jié)果。

總結(jié)與注意事項

  • 大數(shù)處理: 當(dāng)計算結(jié)果可能超出 long 類型的最大值時,務(wù)必使用 BigInteger。這是解決階乘首位數(shù)字判斷問題的根本。
  • 性能優(yōu)化: 備忘錄模式(Memoization)對于涉及重復(fù)子問題計算的場景非常有效。在本例中,它避免了每次都從 1! 開始計算階乘,顯著提升了處理較大范圍或多次查詢時的效率。
  • 首位數(shù)字提取: BigInteger 對象可以直接轉(zhuǎn)換為字符串,然后通過 charAt(0) 提取首位字符,再轉(zhuǎn)換為數(shù)字進行奇偶性判斷。
  • 代碼可讀性 使用 record(Java 16+)可以簡潔地定義數(shù)據(jù)載體,提高代碼的可讀性。如果使用舊版本 Java,可以使用一個普通的類來實現(xiàn) Fact 的功能。

通過上述方法,我們可以健壯且高效地解決在給定范圍內(nèi)查找階乘首位數(shù)字為偶數(shù)的問題,即使涉及大數(shù)階乘計算也能保證準確性。

以上就是使用BigInteger和備忘錄模式高效判斷階乘首位數(shù)字奇偶性的詳細內(nèi)容,更多請關(guān)注php中文網(wǎng)其它相關(guān)文章!

最佳 Windows 性能的頂級免費優(yōu)化軟件
最佳 Windows 性能的頂級免費優(yōu)化軟件

每個人都需要一臺速度更快、更穩(wěn)定的 PC。隨著時間的推移,垃圾文件、舊注冊表數(shù)據(jù)和不必要的后臺進程會占用資源并降低性能。幸運的是,許多工具可以讓 Windows 保持平穩(wěn)運行。

下載
來源:php中文網(wǎng)
本文內(nèi)容由網(wǎng)友自發(fā)貢獻,版權(quán)歸原作者所有,本站不承擔(dān)相應(yīng)法律責(zé)任。如您發(fā)現(xiàn)有涉嫌抄襲侵權(quán)的內(nèi)容,請聯(lián)系admin@php.cn
最新問題
開源免費商場系統(tǒng)廣告
最新下載
更多>
網(wǎng)站特效
網(wǎng)站源碼
網(wǎng)站素材
前端模板
關(guān)于我們 免責(zé)申明 意見反饋 講師合作 廣告合作 最新更新
php中文網(wǎng):公益在線php培訓(xùn),幫助PHP學(xué)習(xí)者快速成長!
關(guān)注服務(wù)號 技術(shù)交流群
PHP中文網(wǎng)訂閱號
每天精選資源文章推送
PHP中文網(wǎng)APP
隨時隨地碎片化學(xué)習(xí)
PHP中文網(wǎng)抖音號
發(fā)現(xiàn)有趣的

Copyright 2014-2025 http://ipnx.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號