ตอนแรกฉันไม่ได้วางแผนที่จะเขียนคำตอบ 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 อื่น ๆ ซึ่งเป็นที่ยอมรับค่อนข้างรัดกุมส่วนใหญ่เพราะผมดำเนินการรุ่นที่สำคัญจากรอยขีดข่วน (และไม่ได้ใช้อัลกอริทึมรายการที่กระชับเข้าใจซุปเปอร์เนื่องจากผลงานไม่ดี) ในขณะที่ถูกนำเข้ามาเพียงแค่จากlcm
Prelude
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)
)
ดังนั้นผลลัพธ์ข้างต้นและการวิเคราะห์อัลกอริธึมสั้นควรทำให้ชัดเจนว่าอัลกอริทึมใดที่เร็วกว่า