มีปัญหาของการเขียนโปรแกรมเพื่อหาจำนวนหลักของ $N!$ อยู่ โดยที่ $N$ นั้นมีค่าเป็นจำนวนมาก จะต้องเขียนโปรแกรมอย่างไรดี? วิธีการทั่วไปก็คือหาค่าของ $N!$ แล้วก็มานับจำนวนหลัก ด้วยการหาร 10 ไปเรื่อยๆแล้วดูว่ามีทั้งหมดกี่หลัก ตามโค้ดด้านล่างนี้
typedef unsigned long long uint64; uint64 factorial(uint64 n) { if(n==0) return 1; else return n * factorial(n-1); }
uint64 numberOfDigits(uint64 n) { uint64 count = 1; while(n > 10) { count++; n /= 10; } return count; }
ฟังก์ชันทั้งสองข้างบน อันแรกคือหาค่าของ $N!$ ด้วยการเขียนแบบ recursive ส่วนฟังก์ชันที่สองก็เป็นการหาจำนวนหลัก จะหาจำนวนหลักของ $N!$ ก็ทำได้โดยการเรียกฟังก์ชันแรกก่อน แล้วนำค่าที่ได้มาส่งให้ฟังก์ชันที่สอง แต่ว่าวิธีการนี้ก็ยังมีข้อจำกัดในเรื่องขนาดของ $N$ อยู่ดีครับ โปรแกรมตัวอย่างผมใช้ unsigned long long (uint64) สำหรับเก็บค่า $N!$ ซึ่งจะเก็บได้ประมาณ 18-19 หลักเท่านั้น ดังนั้นแค่ $N$ มีค่า 20 กว่าๆโปรแกรมก็น็อคแล้ว
วิธีแก้ก็มีหลายแบบครับ ง่ายๆก็เปลี่ยนเป็นภาษาอื่นที่เก็บข้อมูลได้มากขึ้น หรือถ้าใช้ Java ก็อาจจะใช้ BigInteger ก็ว่ากันไป แต่ว่าจริงๆแล้วเราสามารถแก้ปัญหานี้ได้โดยที่ไม่ต้องหาค่าของ $N!$ ก่อนเลยครับ เราสามารถหาจำนวนหลักของ $N!$ ได้ทันทีจากวิธีที่จะเสนอต่อไปนี้
ขั้นแรกคือเราสามารถหาจำนวนหลักของเลขจำนวนเต็มใดๆได้จาก การหา log ฐาน 10 ของมัน ลองดูตัวอย่างครับ
- $log_{10}( 3 ) = 0.477$, $log_{10}( 5 ) = 0.699$
- $log_{10}( 13 ) = 1.114$, $log_{10}( 78 ) = 1.892$
- $log_{10}( 345 ) = 2.538$, $log_{10}( 889 ) = 2.949$
- $log_{10}( 62950 ) = 4.799$, $log_{10}( 11100 ) = 4.045$
จะมองเห็นความสัมพันธ์ของจำนวนหลักกับค่าของ log 10 คือ จำนวนหลักจะเท่ากับ $\lfloor log_{10}(N) \rfloor + 1$ เนื่องจากว่าที่จริงแล้ว log10 ก็คือการหาว่าเลขตัวนั้นจะประกอบขึ้นจากการคูณด้วย 10 กี่ตัวดังนั้นจึงสามารถใช้บอกถึงจำนวนหลักได้เช่นกัน งั้นคราวนี้เรามาเขียนฟังก์ชันหาจำนวนหลักกันใหม่ด้วย
uint64 numberOfDigits2(uint64 n) { if(n==0) return 1; return floor(log10(n)) + 1; }
ทีนี้ถ้าเราจะหาจำนวนหลักของ N! ก็เพียงแต่หาจาก $\lfloor log_{10}(N!) \rfloor + 1$ อ่าว! แล้วจะหา $N!$ ได้ยังไงล่ะในเมื่อถ้าค่า $N$ มากเราก็ยังคำนวน $N!$ ไม่ได้อยู่ดี โอเคครับเราจะไม่คำนวนหา $N!$ เพียงแต่เราจะใช้กฎของ log ข้อนึงที่ว่า $$log(xy) = log(x) + log(y)$$ ดังนั้นแทนที่เราจะหา $log_{10}(N!)$ เราจะหามันจาก $log_{10}(N)$ + $log_{10}(N-1)$ + $log_{10}(N-2)$ + $...$ + $log_{10}(1)$ เราก็จะสามารถหาจำนวนหลักของ $N!$ ได้แล้ว ตามโค้ด(ภาษา C++) ด้านล่างนี้
uint64 factorialDigit(uint64 n) { if(n==0) return 1; long double logSum = 0; for(uint64 i = 1; i <= n; i++) logSum += log10(i); return floor(logSum) + 1; }