ตอนแรกฉันไม่ได้วางแผนที่จะเขียนคำตอบ lcmแต่ผมก็บอกว่าจะหลังจากที่ผู้อื่นทำเรียกร้องแปลกที่เพียงแค่คูณจำนวนเฉพาะคู่แรกที่ได้รับมากขึ้นคอมพิวเตอร์มีราคาแพงแล้วใช้ซ้ำแล้วซ้ำอีก ดังนั้นนี่คืออัลกอริธึมสองตัวและเกณฑ์มาตรฐาน
อัลกอริทึมของฉัน:
อัลกอริธึมการสร้างที่ยอดเยี่ยมทำให้ฉันมีช่วงเวลาที่ไม่สิ้นสุด
isPrime :: Int -> Bool
isPrime 1 = False
isPrime n = all ((/= 0) . mod n) (takeWhile ((<= n) . (^ 2)) primes)
toPrime :: Int -> Int
toPrime n
| isPrime n = n
| otherwise = toPrime (n + 1)
primes :: [Int]
primes = 2 : map (toPrime . (+ 1)) primes
ตอนนี้ใช้รายการเฉพาะเพื่อคำนวณผลลัพธ์สำหรับบางรายการN:
solvePrime :: Integer -> Integer
solvePrime n = foldl' (*) 1 $ takeWhile (<= n) (fromIntegral <$> primes)
ตอนนี้ขั้นตอนวิธีการตาม LCM อื่น ๆ ซึ่งเป็นที่ยอมรับค่อนข้างรัดกุมส่วนใหญ่เพราะผมดำเนินการรุ่นที่สำคัญจากรอยขีดข่วน (และไม่ได้ใช้อัลกอริทึมรายการที่กระชับเข้าใจซุปเปอร์เนื่องจากผลงานไม่ดี) ในขณะที่ถูกนำเข้ามาเพียงแค่จากlcmPrelude
solveLcm :: Integer -> Integer
solveLcm n = foldl' (flip lcm) 1 [2 .. n]
-- Much slower without `flip` on `lcm`
ตอนนี้สำหรับการวัดประสิทธิภาพโค้ดที่ฉันใช้สำหรับแต่ละตัวนั้นง่าย: ( -prof -fprof-auto -O2จากนั้น+RTS -p)
main :: IO ()
main = print $ solvePrime n
-- OR
main = print $ solveLcm n
สำหรับn = 100,000, solvePrime:
total time = 0.04 secs
total alloc = 108,327,328 bytes
vs solveLcm:
total time = 0.12 secs
total alloc = 117,842,152 bytes
สำหรับn = 1,000,000, solvePrime:
total time = 1.21 secs
total alloc = 8,846,768,456 bytes
vs solveLcm:
total time = 9.10 secs
total alloc = 8,963,508,416 bytes
สำหรับn = 3,000,000, solvePrime:
total time = 8.99 secs
total alloc = 74,790,070,088 bytes
vs solveLcm:
total time = 86.42 secs
total alloc = 75,145,302,416 bytes
ฉันคิดว่าผลลัพธ์พูดได้ด้วยตัวเอง
Profiler บ่งชี้ว่ารุ่นที่ใช้เวลาน้อยกว่าและน้อยกว่าเปอร์เซ็นต์ของเวลาทำงานเมื่อnเพิ่มขึ้น ดังนั้นมันจึงไม่ใช่คอขวดดังนั้นเราสามารถเพิกเฉยได้ตอนนี้
ซึ่งหมายความว่าเราเป็นจริงๆเปรียบเทียบโทรlcmที่หนึ่งอาร์กิวเมนต์ไปจาก 1 ถึงnและอื่น ๆ ไปทางเรขาคณิตจาก 1 ansถึง การโทร*ด้วยสถานการณ์เดียวกันและผลประโยชน์ที่เพิ่มขึ้นของการข้ามหมายเลขที่ไม่สำคัญทั้งหมด (asymptotically ฟรีเนื่องจากลักษณะที่มีราคาแพงกว่า*)
และเป็นที่ทราบกันดีว่า*เร็วกว่าlcmที่lcmต้องการแอปพลิเคชั่นซ้ำ ๆ ของmodและmodช้าลงด้วย asymptotically ( O(n^2)vs ~O(n^1.5))
ดังนั้นผลลัพธ์ข้างต้นและการวิเคราะห์อัลกอริธึมสั้นควรทำให้ชัดเจนว่าอัลกอริทึมใดที่เร็วกว่า