本文探討了如何在給定范圍內(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]。
在處理階乘計算時,一個常見的陷阱是使用標準整數(shù)類型(如 int 或 long)來存儲結(jié)果。階乘函數(shù)增長非常迅速:
考慮以下原始代碼片段:
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; }
這段代碼在 a 或 b 較大時會失敗,因為 long 類型的 base 會在 20! 左右溢出。一旦溢出,base 的值將不再代表真實的階乘結(jié)果,后續(xù)通過 ("" + k).charAt(0) 獲取的首位數(shù)字也將是錯誤的。這就是為什么在編碼挑戰(zhàn)中會有隱藏測試用例失敗的原因。
為了解決 long 類型溢出的問題,我們需要使用能夠處理任意精度整數(shù)的類型。在 Java 中,java.math.BigInteger 類正是為此目的設(shè)計的。BigInteger 對象可以存儲任意大小的整數(shù),從而確保階乘計算的準確性,無論數(shù)字有多大。
使用 BigInteger 進行階乘計算的基本思路是:
在給定范圍 [a, b] 內(nèi)查找數(shù)字時,我們可能需要計算多個連續(xù)的階乘(例如 5!、6!、7! 等)。每次都從頭開始計算階乘效率較低。備忘錄模式(或動態(tài)規(guī)劃的自底向上方法)可以顯著提高效率。
其思想是:
我們將創(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] } }
代碼說明:
通過上述方法,我們可以健壯且高效地解決在給定范圍內(nèi)查找階乘首位數(shù)字為偶數(shù)的問題,即使涉及大數(shù)階乘計算也能保證準確性。
以上就是使用BigInteger和備忘錄模式高效判斷階乘首位數(shù)字奇偶性的詳細內(nèi)容,更多請關(guān)注php中文網(wǎng)其它相關(guān)文章!
每個人都需要一臺速度更快、更穩(wěn)定的 PC。隨著時間的推移,垃圾文件、舊注冊表數(shù)據(jù)和不必要的后臺進程會占用資源并降低性能。幸運的是,許多工具可以讓 Windows 保持平穩(wěn)運行。
微信掃碼
關(guān)注PHP中文網(wǎng)服務(wù)號
QQ掃碼
加入技術(shù)交流群
Copyright 2014-2025 http://ipnx.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號