Showing posts with label Python. Show all posts
Showing posts with label Python. Show all posts

Wednesday, October 8, 2008

การเขียนโปรแกรมหาจำนวนเฉพาะโดยใช้ Python

ไหนๆก็ไปโพสท์ไว้ใน blognone แล้ว ก็เลยเอามาแปะไว้นี่ด้วยเลยเผื่อเป็นประโยชน์

ย้ายไปเขียนบน LongSpine.com เนื่องจากโค้ดเละ

วิธีการหาจำนวนเฉพาะ ..แบบเขียนง่ายๆอ่านง่ายๆ


ไอเดียแรกคือการหาจำนวนเฉพาะใหม่จากจำนวนเฉพาะเก่าที่มีอยู่แล้ว โดยมีหลักคือจำนวนเฉพาะนั้นคือจำนวนที่ไม่มีจำนวนเฉพาะอื่นๆหารมันลงตัว

prime = [2]
for i in range(3, 100000):
flag = True # สมมุติว่าจำนวน i เป็นจำนวนเฉพาะ
for j in prime: # จำนวน j ใดๆที่เป็นจำนวนเฉพาะต้องหารจำนวนนี้ไม่ลงตัว
if (i % j == 0): # แต่ถ้าดันหารได้ลงตัว (มีเศษเป็น 0)
flag = False # เราก็พบว่าจำนวนนั้นไม่ใช่จำนวนเฉพาะ
break # ออกจากลูปทันที
if flag == True: # ถ้าพิสูจน์แล้วว่าเป็นจำนวนเฉพาะจริงๆ
prime.append(i) # ให้ใส่ค่านั้นเข้าไปในตัวแปร prime

ไอเดียที่สองคือลดจำนวนตัวที่ต้องวนหารลง เนื่องจากจำนวนเต็มใดๆ j สูงสุดที่จะหาจำนวนเต็มใดๆ i ได้นั้น จะต้องมีค่าไม่มากไปกว่ารากที่สองของ i

prime = [2]
for i in range(3, 100000):
flag = True # สมมุติว่าจำนวน i เป็นจำนวนเฉพาะ
for j in prime: # จำนวน j ใดๆที่เป็นจำนวนเฉพาะต้องหารจำนวนนี้ไม่ลงตัว
if (j ** 2 > i): # ถ้า j มีค่ามากกว่าตัวหารสุงสุดที่เป็นไปได้ (มีค่ามากกว่ารากที่สองของ i)
break # ให้ออกจากลูป
if (i % j == 0): # แต่ถ้าดันหารได้ลงตัว (มีเศษเป็น 0)
flag = False # เราก็พบว่าจำนวนนั้นไม่ใช่จำนวนเฉพาะ
break # ออกจากลูปทันที
if flag == True: # ถ้าพิสูจน์แล้วว่าเป็นจำนวนเฉพาะจริงๆ
prime.append(i) # ให้ใส่ค่านั้นเข้าไปในตัวแปร prime


ไอเดียที่สาม เรายังทำให้เร็วได้มากไปกว่านี้อีก โดยใช้วิธีซีฟของเอราทอสเทนีส ซึ่งใช้หน่วยความจำมาช่วยในการคำนวน (อ่านรายละเอียดได้ตามลิงค์) วิธีนี้ข้อเสียคือเปลืองหน่วยความจำมาก

import numpy

n = 100000 # หาจำนวนเฉพาะที่มีค่าน้อยกว่า 100000

primeArr = numpy.zeros(n) # สร้าง array ขนาด n
primeArr[0] = 1 # 0 ไม่ใช่จำนวนเฉพาะ
primeArr[1] = 1 # 1 ก็ไม่ใช่

for i in xrange(2, n):
if primeArr[i] == 0: # ถ้าจำนวนนั้นเป็นจำนวนเฉพาะ
for j in xrange(i * 2, n, i): # จำนวนอื่นๆที่จำนวนนั้นหารลงตัว
primeArr[j] = 1 # ..จะไม่ใช่จำนวนเฉพา

prime = []

for i in xrange(2, n):
if primeArr[i] == 0: # เก็บจำนวนเฉพาะไว่ในตัวแปร prime
prime.append(i)
ทดลองเปรียบเทียบความเร็วของทั้งสามตัวอย่าง

poomk@gemini:/tmp$ time ./prime1.py >0

real 0m19.622s
user 0m19.581s
sys 0m0.020s

poomk@gemini:/tmp$ time ./prime2.py >0

real 0m0.675s
user 0m0.676s
sys 0m0.000s

poomk@gemini:/tmp$ time ./prime3.py >0

real 0m0.535s
user 0m0.504s
จะเห็นว่าเร็วขึ้นตามลำดับ

ไอเดียที่สี่ เนื่องจากตัวอย่างที่สามนั้นไม่เหมาะกับการหาจำนวนเฉพาะที่มีขนาดมากๆ (มากๆๆๆๆ...ๆ) ทำให้เราไม่สามารถใช้วิธีของซีฟได้เพราะเรามีหน่วยความจำไม่เพียงพอ วิธีหนึ่งที่(ผมเคย)ใช้ก็คือการหาจำนวนเฉพาะตั้งต้นด้วยซีฟ(ตัวอย่างที่สาม) จากนั้นค่อยน้ำตัวเฉพาะตั้งต้นเหล่านั้นไปหาตัวเฉพาะอื่นๆต่อตามตัวอย่างที่สอง

import numpy

n = 1000000 # หาจำนวนเฉพาะที่มีค่าน้อยกว่า 1000000 (หนึ่งล้าน)

primeArr = numpy.zeros(n)
primeArr[0] = 1
primeArr[1] = 1

for i in xrange(2, n):
if primeArr[i] == 0:
for j in xrange(i * 2, n, i):
primeArr[j] = 1

prime = []

for i in xrange(2, n):
if primeArr[i] == 0:
prime.append(i)

for i in range(n, 2000000): # หาต่อจนถึงตัวที่ 2000000 (สองล้าน)
flag = True
for j in prime:
if (j ** 2 > i):
break
if (i % j == 0):
flag = False
break
if flag == True:
prime.append(i)
เมื่อจับเวลาก็จะเห็นความแตกต่าง
poomk@gemini:/tmp$ time ./prime2.py >0

real 0m27.190s
user 0m27.090s
sys 0m0.096s

poomk@gemini:/tmp$ time ./prime4.py >0

real 0m20.422s
user 0m20.369s
sys 0m0.040s

อนึ่งโปรแกรมตัวอย่างนี้เขียนขึ้นเพื่อความอ่านง่าย (และมักง่าย) มีจุดประสงค์เพื่อบอกแนวโน้มของความเร็วที่เพิ่มขึ้นของแต่ละไอเดีย ซึ่งหากเขียนไปใช้งานจริงๆอาจจะมีลูกเล่นที่ทำให้ทำเวลาได้ดีขึ้นกว่านี้อีก

Thursday, December 6, 2007

C/C++ Performance in Complex Number Computation

จากที่คุณ bow_der_kleine ได้เขียนใน biolawcom.de ไว้ในหัวข้อ C/C++ vs. Fortran in Scientific Programming ทำให้ผมเกิดข้อสงสัยในการเปรียบเทียบความเร็วระหว่าง C/C++/Fortran ในการคำนวณ complex number

ในตัวอย่างนั้นได้ทำการเปรียบเทียบ C++ และ Fortran ในการคำนวณด้าน complex number ไว้ และสรุปว่าภาษา C นั้นช้ากว่าโปรแกรมที่เขียนโดย Fortran มากๆ ในการคำนวณ complex number
ผมค่อนข้างไม่เห็นด้วยกับข้อสรุปนี้ เนื่องจากยังไม่มีการเขียนเปรียบเทียบระหว่างภาษา C (ชัดๆ) และ Fortran เลย

ข้อแตกต่างระหว่าง c/c++ นอกจากความสามารถด้านคลาสแล้วยังรวมถึงไลบรารีต่างๆที่ใช้ด้วย และหนึ่งในนั้นคือไลบรารี complex ซึ่งใน C/C++ นั้นมีรายละเอียดแตกต่างกันค่อนข้างมาก

ผมลองเขียนโค้ดโดยอิงตัวแปรจากคุณ bow เป็นหลัก มาสามโปรแกรมคือ
  1. ภาษา C ที่เรียกใช้ complex.h ซอร์ส
  2. ภาษา C++ ที่เรียกใช้ complex ซอร์ส
  3. ภาษา C ที่ไม่เรียกใช้ complex.h ซอร์ส
และทำการจับเวลาโดยใช้สคริปท์ python

ได้ผลลัพท์คือ (ดูรูปใหญ่)



จะเห็นว่าเวลาที่ได้ระหว่าง C/C++ นั้นมันต่างกันอย่างเห็นได้ชัด โดยมีอัตราการโตของเวลาที่ใช้แตกต่างกัน

จริงๆแล้วอยากเทียบกับ Fortran จังเลย แต่เขียนไม่เป็น

ปล. ระหว่างที่เขียนและรันโปรแกรมได้โหลดบิททอเรนท์อยู่ ผลลัพท์อาจผิดพลาดไปเล็กน้อย

Wednesday, July 18, 2007

Install Python2.5.1 on Solaris 10

1. ดาวน์โหลด python2.5.1 package จากเว็บ sunfreeware.com หากดาวน์โหลดไม่ได้ ให้ลองเข้า http://www.download-ftp-center.com/sunfreeware.com/sparc/10/ แทน

2. คำสั่งติดตั้งมาตรฐาน
gunzip python-2.5.1-sol10-sparc-local.gz 
pkkadd -d python-2.5.1-sol10-sparc-local
3. เมื่อลงเสร็จแล้ว ถ้าต้องการที่จะเรียกโปรแกรมได้โดยไม่ต้องใส่ PATH ให้แก้โดยใช้คำสั่ง
PATH=$PATH:/usr/local/bin
ถ้าต้องการใช้คำสั่งนี้ทุกครั้งที่ทำงาน ให้เพิ่มโค้ดบรรทัดหนึ่ง ลงไปใน .profile
PATH=$PATH:/usr/local/bin
4. ตรวจสอบเวอร์ชันของ python โดยใช้คำสั่ง
python -V
5. ในกรณีที่คำสั่ง python มันไปเรียก python อีกเวอร์ชันหนึ่ง เช่น python2.3 ให้ทำการตามลบ link ที่ชี้ไปยัง python เวอร์ชันเก่า
which python
rm <ไฟล์ที่ which แสดง>
ง่ายเนอะ