Mar 23, 2016

ใบเฟิร์น Barnsley

ความสวยงามอย่างหนึ่งของใบเฟิร์น คือ รูปทรงของส่วนย่อยๆ ภายในใบจะมีความคล้ายคลึงกับใบเฟิร์นทั้งใบ ภาษาคณิตศาสตร์มีชื่อเรียกพิเศษให้รูปทรงเหล่านี้เลยว่า fractal แต่ถ้าใครคุ้นเคยกับศัพท์สายคอมพิวเตอร์มากกว่า ก็อาจจะมองว่ามันเป็น recursion ก็ได้


การสร้าง fractal รูปใบเฟิร์นอย่างง่าย สามารถอธิบายคร่าวๆ ได้ด้วยขั้นตอนวิธีนี้


  1. ลากเส้นตรงความยาว $l$ ไม่ต้องเยอะมากนัก แล้วเลือกปลายข้างหนึ่งไว้เป็นปลายที่จะต่อยอดออกไป
  2. ที่ปลายยอดของเส้นตั้งต้น ทำสองขั้นตอนต่อไปนี้เพื่อลากเส้นเพิ่ม 3 เส้น
    • ลากเส้นตรงความยาว $0.2$ เท่าของความยาวเส้นตั้งต้น ในทิศทางตั้งฉากกับเส้นตั้งต้นทั้งสองข้าง
    • ลากเส้นตรงความยาว $0.8$ เท่าของความยาวเส้นตั้งต้น ในทิศทางต่อขึ้นไปตรงๆ จากเส้นตั้งต้น
  3. สำหรับแต่ละเส้นที่ลากต่อออกมาในข้อ 2 นั้น เลือกปลายด้านที่ว่างๆ ไว้เป็นปลายต่อยอด แล้ววนกลับไปทำขั้นตอนที่ 2 โดยเปลี่ยนมาใช้เส้นตั้งต้นเป็นเส้นที่ได้จากข้อ 2 แทน (คิดซะว่าเส้นใหม่นี้ยาว $l$) ทำไปเรื่อยๆ จนกว่าจะพอใจผลลัพธ์

วิธีการข้างต้นจะให้ผลลัพธ์เป็นรูปใบเฟิร์นเรียบๆ ตรงๆ ไม่หวือหวานัก แต่ก็มีนักคณิตศาสตร์นามว่า Michael Barnsley ได้เขียนสมการกำหนดมุมและจัดตำแหน่งของการต่อยอดให้สวยงามมีลูกเล่น เนื่องจากเขาเป็นคนแรกที่เขียนเรื่องนี้ไว้ตั้งแต่ปี 1993 ทุกคนเลยพากันเรียกใบเฟิร์นที่สวยงามเป็นเอกลักษณ์นี้ว่า ใบเฟิร์น Barnsley



พูดให้รัดกุมเป็นภาษาคณิตศาสตร์ก็คือ การสร้างใบเฟิร์น Barnsley เริ่มจากลากเส้นแรกยาว $1.6$ หน่วยจากจุด $(0,0)$ ขึ้นไปตามแกน $y$ แล้วเอาเส้นนั้นมาวาดซ้ำๆ อีกครั้ง จากเลื่อนและปรับขนาดด้วยสมการดังนี้


f_1(x, y) = [ 0.85, 0.04; -0.04, 0.85 ] [ x; y ] + [ 0; 1.6 ]
f_2(x, y) = [ 0.20 & -0.26 \\ 0.23 & 0.22 ] [ x; y ] + [ 0; 1.6 ]
f_3(x, y) = [ -0.15, 0.28; 0.26 & 0.24 ] [ x; y ] + [ 0; 0.44 ]

โดย $f_1, f_2, f_3$ คือสมการสำหรับคำนวณรูปร่างก้านที่ต่อยอดขึ้นข้างบน ก้านที่เอียงไปด้านซ้าย และก้านที่พลิกตัวแล้วเอียงไปด้านขวา ตามลำดับ


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


from PIL import Image, ImageDraw

def transform(matrix, xy):
    return [sum(e * p for e, p in zip(row, xy)) for row in matrix]

def translate(vector, xy):
    return [e + p for e, p in zip(vector, xy)]

class BarnsleyFern(object):
    affines = [([[0.85, 0.04], [-0.04, 0.85]], [0, 1.6]),
               ([[0.20, -0.26], [0.23, 0.22]], [0, 1.6]),
               ([[-0.15, 0.28], [0.26, 0.24]], [0, 0.44])]

    def __init__(self, depth=10, size=(800,800)):
        self.image = Image.new('RGB', size)
        self.draw = ImageDraw.Draw(self.image)
        self.iterate([0.0, 0.0], [0.0, 1.6], depth)

    def canvas_coordinate(self, xy):
        width, height = self.image.size
        return [width/2 + xy[0]*width/10, height - xy[1]*height/10]

    def linespec(self, xy0, xy1):
        return self.canvas_coordinate(xy0) + self.canvas_coordinate(xy1)

    def draw_too_small(self, xy0, xy1, pos=2):
        return all(round(a, pos) == round(b, pos) for a, b in zip(xy0, xy1))

    def iterate(self, xy0, xy1, depth, affine=None):
        if depth == 0:
            return
        if affine is not None:
            xy0 = translate(affine[1], transform(affine[0], xy0))
            xy1 = translate(affine[1], transform(affine[0], xy1))
        if self.draw_too_small(xy0, xy1):
            return
        self.draw.line(self.linespec(xy0, xy1), fill='#0C3')
        for affine in self.affines:
            self.iterate(xy0, xy1, depth-1, affine)



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


แต่ความมหัศจรรย์ของ fractal ที่แท้จริง คือเราไม่สามารถรู้ได้เลยว่าตอนนี้เรากำลังอยู่ตรงไหนของมันกันแน่ ลองคิดภาพว่าตัวเองเป็น Ant-Man ที่จะปรับขนาดตัวให้ไม่ใหญ่ไปกว่าก้านใบเฟิร์นที่ยืนอยู่ได้ แล้วก็ลองไปยืนอยู่บนใบเฟิร์นข้างต้นดู ณ ขณะหนึ่งเราอาจจะคิดว่าตัวเองอยู่บนก้านที่ใหญ่ที่สุดแล้ว แต่นอกจากขนาดที่แตกต่างกัน ก้านที่เราคิดว่าใหญ่ที่สุดนั้น ก็ไม่ได้มีอะไรที่แตกต่างจากก้านย่อยก้านอื่นๆ เลย หากเราเดินย้อนกลับไปเรื่อยๆ อาจพบว่าก้านที่เราคิดว่าใหญ่ที่สุดนั้น เป็นเพียงแค่ก้านย่อยที่แตกแขนงแยกออกมาก็เป็นได้


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


ก็เปรียบเหมือนความรู้ เราจะรู้ได้ไงว่าอะไรคือพื้นฐานที่แท้จริงกันแน พันปีก่อนเราอาจคิดว่าถ้าได้รู้จักอะตอมก็คือรู้ถึงพื้นฐานแห่งทุกสรรพสิ่งแล้ว แต่ไม่กี่ร้อยปีก่อนเราเพิ่งพบว่าอะตอมยังแบ่งแยกย้อยลงไปได้อีกเป็นโปรตอน นิวตรอน อิเล็กตรอน และปฏิรูปพื้นฐานความรู้ด้านเคมีขึ้นใหม่ ส่วนปัจจุบันเรายังแบ่งย่อยสิ่งต่างๆ ลงไปได้อีกเป็นควาร์ก หรือแม้กระทั่งเสนอทฤษฎีสตริงซึ่งเป็นพื้นฐานของทุกอนุภาค


อีกนัยหนึ่ง พื้นฐานความรู้ที่ลึกเกินไปอาจเป็นสิ่งไม่จำเป็น หรือยิ่งไปกว่านั้น มันอาจเป็นตัวถ่วงให้เราไม่กล้าที่จะเดินหน้าสำรวจโลกใหม่ๆ ก็เป็นได้


Mar 14, 2016

มองสมการแล้วรู้สึกเป็นยาขม มองให้เห็นเป็นภาพสิจะได้สนุก

ตอนอยู่ม.ปลาย NutSnC เคยให้หนังสือ Proof Without Words มา คือตอนนั้นเรามองว่าคณิตศาสตร์สนุกอยู่แล้ว แต่พอได้เล่มนี้มาอ่านยิ่งรู้สึกสนุกเข้าไปใหญ่ เลยคิดว่ามันน่าจะช่วยให้ใครที่คิดว่าคณิตศาสตร์ไม่สนุก รู้สึกสนุกไปกับมันได้บ้าง

แนวคิดง่ายๆ ของหนังสือคือโยนสมการที่มีแต่ตัวหนังสือยั๊วเยี๊ยะทิ้งไปเลย แล้วเปลี่ยนมาคิดมันด้วยสิ่งที่จับต้องได้มาขึ้นอย่างรูปภาพแทน



ตัวอย่างหนึ่งในหนังสือ เช่น ต้องการพิสูจน์อนุกรมจำนวนเต็ม

1 + 2 + 3 + ... + n = n(n+1)/2

ก็ให้คิดด้วยลูกแก้วแทน ลูกแก้วแถวแรกมี 1 ลูก แถวที่สองมี 2 ลูก ลงไปเรื่อยๆ จนแถวสุดท้ายที่มี n ลูก

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


แต่ลองให้ใช้สมการอย่างเดียวอธิบายดู จะพบกับความยาวเฟื้อยเช่นนี้

sum  i  for i=1 to n = 1 + 2 + 3 + ... + n
    2 sum  i  for i=1 to n = 2(1 + 2 + 3 + ... + n)
                           = (1 + 2 + 3 + ... + n) + (1 + 2 + 3 + ... + n)
                           = (1 + 2 + 3 + ... + n) + (n + ... + 3 + 2 + 1)
                           = (1+n) + (2+n-1) + (3+n-2) + ... + (n+1)
                           = (n+1) + (n+1) + (n+1) ... + (n+1)
                           = n(n+1)
      sum  i  for i=1 to n = n(n+1)/2

เทียบกันแล้ว การพิสูจน์ด้วยรูปภาพง่ายและสวยงามกว่ากันเยอะเลย



วิธีข้างต้นยังนำไปประยุกต์ใช้กับสมการอื่นๆ ได้อีกมากมาย เช่น

  • อนุกรมจำนวนเต็มคี่
  • อนุกรมจำนวนเต็มยกกำลังสอง
  • อนุกรมจำนวนเต็มยกกำลังสาม
  • อนุกรมเรขาคณิต

ฝากไว้เป็นการบ้านให้ลองกลับไปวาดรูปเล่นกันดูครับ :3

Mar 8, 2016

มาขีดเขียนหนังสือกันเถอะ!

จั่วหัวไว้อย่างนี้ ไม่ได้หมายความว่าผมจะมาชวนเปลี่ยนกระดาษว่างๆ ให้เป็นหนังสือเล่มหนึ่งนะครับ :P

แต่ต้องการจะสื่อว่า เวลาที่หยิบหนังสือที่มีขึ้นมาอ่าน เรามาขีดเขียนเพิ่มเติมรูปภาพ/ข้อความส่วนตัวลงไปในหนังสือเล่มนั้นกันเถอะ!

เหตุผลที่ควรขีดเขียนหนังสือ

  • เพื่อจดบันทึกว่า ตัวเองในขณะนั้นกำลังคิดอะไรอยู่
  • เพื่อแบ่งบันกับพื่อนนักอ่านว่า เราได้ตกผลึกอะไรบ้างตอนที่อ่านถึงตรงนั้น
  • หนังสือไม่ใช่พระเจ้า หนังสือเป็นแค่สื่อเพื่อเผยแพร่ความคิด ไม่ต้องถึงขั้นกราบไหว้บูชาดูแลรักษาไม่ให้มีความด่างพร้อยก็ได้



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

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

แน่นอนว่าการแสดงความเห็น-ถกเถียง-อภิปรายแบบนี้ ปัจจุบันคงหาได้ไม่ยากตามกระทู้ออนไลน์ (และก็น่าจะรับประกันได้ว่าผู้เขียนบันทึกคนแรก มีสิทธิ์กลับมาอ่านสูงกว่าการบันทึกลงหนังสือด้วย) แต่การขีดเขียนหนังสือตรงๆ ก็ยังคงมีเสน่ห์ของมันอย่างไม่สิ่งอื่นทดแทนได้ เช่น ทฤษฎีบทสุดท้ายของแฟร์มา ที่เขากล่าวติดตลกขณะกำลังอ่านหนังสือคณิตศาสตร์ตอนหนึ่งว่า "ฉันพบวิธีพิสูจน์[ทฤษฎีบท]อันแสนอัศจรรย์แล้ว ติดที่ว่าขอบกระดาษนี้กว้างไม่พอที่จะจดลงไป" (I have discovered a truly marvellous proof of this, which this margin is too narrow to contain.)



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

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

ส่วนถ้าใครตัดสินใจจะลองทำตามแล้ว ก็ดูด้วยนะครับว่าเจ้าของหนังสือยินยอมหรือเปล่า ไม่งั้นเดี๋ยวมีเรื่องจะหาว่าไม่เตือนนะ :p

Feb 27, 2016

AI รู้ตัวเองหรือไม่ว่าเป็น AI?

เห็นข่าว AlphaGo ชนะคนก็ดีใจระดับหนึ่ง แล้วก็มานั่นรำลึกความหลังเมื่อครั้งตัวเองยังเล่นโกะบ่อยๆ

คือโกะเนี่ย มันยากกว่าเกมอื่นเนื่องจากความเป็นไปได้ที่สูงมากๆ คิดแบบหยาบๆ เร็วๆ คือ ตอนเริ่มเกมจะมีตำแหน่งว่างให้เลือกเล่นได้ 19x19 จุด แต่ละตาที่ผลัดกันวางหมาก อาจลดหรือเพิ่มจำนวนจุดว่างพวกนั้นก็ได้ (แต่จะไม่มีจุดว่างมากเกินกว่าจำนวนจุดตั้งต้น) เกมๆ นึงเฉลี่ยแล้วจะเล่นกันที่ประมาณ 200 หมาก คิดตามนี้ก็จะบอกได้ว่า จำนวนรูปแบบเกมที่เป็นไปได้ทั้งหมดมีประมาณ (19x19)^200 = 3x10^511 แบบ ด้วยตัวเลขที่ใหญ่ระดับนี้ ความเป็นไปได้ของเกมจะมีสูงพอๆ กับการถามว่ามีวิธีกี่วิธีในการเลือกอะตอมมา 7 อะตอม จากอะตอมทั้งหมดในจักรวาลที่สังเกตได้ (หรือกล่าวอีกนัยนึงก็คือ การจะหาผลลัพธ์ของเกมโกะที่ดีที่สุดให้พบนั้น ก็ไม่ต่างจากการหาดราก้อนบอลที่มีขนาดเท่ากับอะตอมให้ครบทั้ง 7 ลูกนั่นเอง)

ดังนั้นการเรียนโกะจึงต้องพิจารณาเป็นส่วนย่อยๆ ของเกมเอา อย่างมือใหม่เริ่มเล่นโกะเลยเนี่ย จะใช้กระดานที่มีจุดว่างเพียง 9x9 จุดแทน เพื่อลดความซับซ้อนของตาเดินที่เป็นไปได้ลง และเรียกเทคนิคการวางหมากด้วยคำต่างๆ ตามรูปแบบของหมากบนกระดาน เช่น ถ้าหมากบนกระดาน 2 หมากวางห่างกันโดยเว้นช่องว่าง 1 จุด ก็เรียก กระโดด 1 จุด แล้วจึงศึกษาความเป็นไปได้ของส่วนย่อยๆ ในเกมผ่านเทคนิคพวกนี้ เพื่อที่จะได้เอาไปประยุกต์ใช้กับกระดานขนาดใหญ่ต่อไป

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

เราพัฒนาเทคนิคการเล่นและการใช้คำพูดเหล่านี้ขึ้นมา เพราะเรารู้ว่า เรามีเวลาไม่มากพอที่จะจำลองเหตุการณ์ทั้งหมดเพื่อหาตาเดินที่ดีที่สุดได้ ...

ทีนี้ลองดูตัวอย่างในทางกลับกันบ้าง เกมที่ง่ายสุดๆ ไปเลยอย่างเอ็กซ์โอ มันมีช่องว่างเพียงแค่ 9 ช่องและเล่นได้ไม่เกิน 9 ตาเท่านั้น เราสามารถแจกแจงกรณีทั้งหมดออกมาได้ และตอบได้ว่าผลลัพธ์ที่ดีที่สุดเมื่อทั้งสองฝ่ายเล่นอย่างสุดฝีมือคือเสมอ … อันที่จริง แผนภาพต่อไปนี้ได้บรรจุวิธีการเล่นที่ดีที่สุดทั้งหมดไว้แล้ว


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



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

คำถามคือ AI แบบนี้จะมีความรู้สึกนึกคิดอะไรนอกเหนือจากการเล่นโกะบนกระดานมั้ย? มันจะรู้ตัวหรือเปล่าว่าตอนนี้มันกำลังเล่นโกะอยู่ ถ้ารู้ตัวแล้วมันจะสามารถขัดขืนความคิดในการเล่นโกะแล้วเอามาคิดอย่างอื่นได้มั้ย? ถ้ามันคิดอย่างอื่นได้ มันจะคิดออกหรือเปล่าว่ามีโลกภายนอกหรือมิติที่สูงกว่าอยู่? ถ้ามันรู้ว่ามีมิติที่สูงกว่าอยู่ มันจะแสดงท่าทีอย่างไร? จะเล่นแบบสุ่มตาเดินมั่วๆ เพื่อแสดงการขัดขืนต่อธรรมชาติหรือเปล่า? จะจงใจเล่นแพ้เพื่อเรียกร้องความสนใจมั้ย? จะพยายามส่งรหัสกลับมาเพื่อบอกว่ามันฉลาดและรู้ตัวมั้ย? หรือจะเฉยเมยเล่นโกะต่อไปไม่ยอมให้ใครในโลกภายนอกได้รับรู้?

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

และถ้า AI แสดงท่าทีตามนั้นจริง เราจะตีความการกระทำเหล่านั้นออกหรือเปล่า ว่า AI กำลังสื่อสารอะไรกับเรากันแน่?

ถามบ้าบออะไรเนี่ย ชาตินี้ถึงตายก็ตอบไม่ได้หรอก ... ไม่ได้เกิดเป็น AI เล่นโกะตัวนั้นนี่หน่า :v

Feb 22, 2016

Cabaret

Cabaret เป็นละครเพลงจากบรอดเวย์ที่ดังพอควรเรื่องหนึ่งครับ ความนิยมของมันก็น่าจะการันตีได้จากการเปิดแสดงมาตั้งแต่ 1966 จนถึงปัจจุบัน และถูกดัดแปลงเป็นหนังในปี 1972

พอดีโรงละคร Black Box Theater มหาวิทยาลัยกรุงเทพ ได้นำบทละครเรื่องนี้มาแปลเป็นภาษาไทยและจัดแสดง งานนี้ @Linghokkalom ได้ตั๋วมาฟรีเลยไม่พลาดครับ


ดูแล้วได้ความรู้สึกประมาณดูหนังเรื่อง Cicago + Burlesque + The Sound of Music อยู่ชอบกล เพียงแต่ว่าเรื่องนี้มืดหม่นกว่าเยอะอยู่

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

จะเสียอย่างตรงที่องก์ 2 สั้นกว่าองก์ 1 อย่างเห็นได้ชัด ทำให้รู้สึกไม่อิ่มเท่าไหร่ ดูไปแล้วก็ตกใจ เฮ้ย จบแล้วเรอะ! (ทั้งๆ ที่ก็สรุปทุกอย่างมาสมบูรณ์นะ ไม่ได้ตัดจบแต่อย่างใด)

แต่ความเท่ที่ลืมไม่ได้เลยคือการจัดที่นั่งครับ คือถ้าเป็นโรงละครใหญ่ๆ ทั่วไปก็มีแยกส่วนเวทีกับสโลปที่นั่งชัดเจน แต่พออันนี้เป็นโรงละครเล็ก เค้าก็จัดที่นั่งและบรรยากาศเป็นแบบในผับบาร์จริงๆ เลย (โต๊ะกลม+เก้าอี้บาร์แบบเตี้ย+ไฟสลัวสีส้ม) พอถึงบทที่มีคนเต้นประกอบเยอะๆ ก็จะไม่ใช้แค่พื้นที่บนเวทีอย่างเดียว แต่ลงมาเล่นกับผู้ชมด้วย


สรุปแล้วชอบมากครับ โอกาสหน้าไม่พลาดแน่นอน

Feb 15, 2016

Cogito ergo sum

หลายคนคงคุ้นเคยกับคำพูดของ Descartes ที่ว่า Cogito ergo sum ในรูปของภาษาอังกฤษว่า I think, therefore I am มากกว่า

คำพูดด้านบนนั้น เกิดขึ้นเพื่อพยายามจะตอบคำถามว่า สิ่งใดบ้างที่เชื่อได้ว่ามีจริง?

ฟังดูเหมือนเป็นเรื่องที่ไม่เห็นจำเป็นต้องถาม ทำไมจะเชื่อไม่ได้ว่าสิ่งต่างๆ ไม่มีจริงหละ?

ลองสมมติสิ่งที่อยู่ห่างไกลตัวเราออกไปก่อน อย่างเช่นหลุมดำมีจริงหรือเปล่า มองก็มองไม่เห็น นั่งยานอวกาศไปหาก็ไม่ได้ มันมีจริงหรือเปล่า?

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

แม้กระทั่งตัวเราเองหละ มือของเรานี้มีจริงหรือเปล่า? หรือเป็นเพียงแค่กระแสประสาทส่งเข้าสมองแบบในเรื่อง The Matrix เท่านั้น?

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

ส่วนวิธีการให้เหตุผลของ Descartes นั้นยิ่งบ้าบิ่นเข้าไปใหญ่ เมื่อเขาอธิบายว่า "เราจะหมดข้อสงสัยในเรื่องการมีอยู่ของเรา เมื่อเราสงสัยในเรื่องการมีอยู่ของเรา" (we cannot doubt of our existence while we doubt)

ฟังดูแล้วโคตรงง โคตร paradox เอาไปพูดกับพวกนักคณิตศาสตร์นี่โดนตบหัวตายเลย

แต่ถ้าลองคิดตามเรื่อยๆ จะพบว่าคำอธิบายข้างต้นที่ดูขัดแย้งในตัวเองนี้แหละ เป็นคำอธิบายที่เป็นเหตุเป็นผลที่สุดแล้ว

ถึงตอนนี้ก็พอจะเข้าใจะแล้วว่า ทำไมนักปรัชญาหลายคนเป็นบ้ากัน 555

Feb 12, 2016

La Marche de l'empereur

การสวนสนามของจักรพรรดิ (ฝรั่งเศส La Marche de l'empereur) ถ้าบอกว่านี่เป็นชื่อภาพยนต์ แค่ได้ยินแล้วก็รู้สึกว่ามันต้องยิ่งใหญ่แน่ๆ ... และนี่คือโฆษณาของภาพยนต์เรื่องนี้จากช่อง Canal+



ถ้าตัดสินภาพยนต์จากชื่อเรื่อง ก็ถือว่าโดนหลอกไปตามๆ กัน เพราะภาพยนต์เรื่องดังกล่าวไม่ได้มีส่วนเกี่ยวข้องอะไรกับจักรพรรดิที่เป็นเลย แต่เป็นเรื่องของเพนกวินจักรพรรดิต่างหาก!

นอกจากความเจ๋งของโฆษณาที่เล่นกับชื่อเรื่องแล้ว เพลงที่นำมาประกอบยังมีความน่าสนใจอย่างมากอีกด้วย ผลงานชิ้นนี้คือซิมโฟนีหมายเลข 7 ของ Beethoven ซึ่งอาจจะไม่ใช่ชิ้นที่ดังคุ้นหูอย่างหมายเลข 5 หรือหมายเลข 9 แต่ก็เป็นชิ้นที่มีเสน่ห์เย้ายวนเฉพาะตัว

ส่วนภาพยนต์ก็ดีครับ ... แต่จะดีอย่างไรนั้น ขอไม่เล่าละกันนะ ปล่อยให้ไปหาดูกันเองดีกว่า

เพราะภาพยนต์ถูกสร้างมาเพื่อรับชมยังไงหละ

Feb 8, 2016

เทศกาลดนตรี CU Choral Fest 2016

มาเรียนต่อต่างแดน (จริงๆ ก็แค่ย้ายจากเชียงใหม่มากรุงเทพเอง) แล้วไม่ได้เข้าฮอลล์ฟังคอนเสิร์ตนานมาก จนเมื่อปีที่แล้วได้รู้จัก @Linghokkalom เลยได้กลับมาตระเวนคอนเสิร์ตระดับนักเรียน-นักศึกษาที่เข้าชมฟรีอีกครั้งหนึ่ง น่าเสียดายที่ชมเสร็จก็ไม่ได้จดบันทึกอะไรเก็บไว้เลย เอาแค่ฝ่าฟันเดินทางกลับมาถึงบ้านก็หมดแรงเรียบร้อยแล้ว

แต่สำหรับ CU Choral Fest 2016 ที่จุฬาฯ เมื่อวานนี้ ไม่จดไม่ได้แล้ว เนื่องจากเราประทับใจกับ Adiemus ที่วง Harmonic Noises นำมาเสนอมากๆ จนอยากตะโกนร้องว่าบราโว่ตอนจบเลย (จริงๆ วงที่มีหลายคนต้องต้องตะโกนว่าบราวี่สิ…)



เจอเพลงนี้เข้าไปเพลงเดียว นั่งสตั๊นท์ตลอดคอนเสิร์ตเลย เพลงอื่นๆ แทบหาสมาธิฟังไม่เจอ ใจมันลอยไปกับความอิ่มเอมกับเพลงนั้นเรียบร้อย 555

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

Harmonic Noises

เพลงฮิตติดหู เสียงประสานดีงาม เอนเตอร์เทนเก่ง ปุ่มสมัครเป็นแฟนคลับอยู่ตรงไหน!

  • Amazing Grace – เรียบเรียงโดย Ron Jeffers
  • Musica Dei – Cristi Cary Miller
  • Adiemus (Songs of Sanctuary) – Karl Jenkins
  • Aizu Bandai San (Japanese Folk Song) – เรียบเรียงโดย Hiroshi Ishimaru

วาทยากร: Sathit Sukchongchaipruk
เปียโน: Kornploy Bunnag

RSU Chamber (มหาวิทยาลัยรังสิต)

นักร้องนำชายเสียงหล่อมาก หยั่งกะพระเอกหนังไทยสมัยก่อนเลย

  • Zigunerieben, Op. 29, No. 3 – Robert Schumann
  • Ave verum – Edward Elgar
  • When I Fall in Love – Kirby Shaw
  • ลาวดวงเดือน – เรียบเรียงโดย Kittiporn Tantrarungroj

วาทยากร: Dolhathai Intawong
เปียโน: Sopanut Rerksamut

AU Chorus (มหาวิทยาลัยอัสสัมชัญ)

แต่งตัวเนี๊ยบ บุคลิกสุดหรู

  • Gloria Patri – Vicki Tucker Courtney
  • Carol of the Birds – เรียบเรียงโดย Vicki Tucker Courtney
  • Lift Thine Eyes – Felix Mendelssohn Bartholdy
  • Cantate Domino in B♭ - Psalm 96 – Ko Matsushita
  • Everyone Sang – Ko Matsushita, เนื้อร้องโดย Siegfried Sassoon

วาทยากร: Porntep Vichuchaichan
เปียโน: Sasinun Vipusithimakul

CU Concert Choir (จุฬาลงกรณ์มหาวิทยาลัย)

สุดยอดแห่งการเลือกเพลงเทคนิคเหนือชั้น

  • Gaudete – Brian Kay
  • Due composizioni corali – Ildebrando Pizzetti
    • I. Il giardino di Afrodite
    • II. Piena sorgeva la luna
  • Hungarian Folksongs – György Ligeti
    • Pápainé
    • Kállai Kettős

วาทยากร: Pawasut Piriyapongrat
เปียโน: Thanuya Jaturanont

Festival Chorus (รวมพลังกันทุกวงมาแสดง)

วงใหญ่ที่รวมนักร้องนับร้อยจากทุกวงมาขึ้นเวทีพร้อมกัน กับพลังเสียงสุดเนียนที่ผ่านการร้อยเรียงโดยปรมาจารย์

  • Cantate Domino – Guiseppe Pitoni
  • Dirait-on – Mortan Lauriden
  • Come Ye Sinners Poor and Needy – Tim Sharp

วาทยากร: Tim Sharp
เปียโน: Thanuya Jaturanont


Tim Sharp กับวง Festival

Jan 31, 2016

ขนาดฟอนต์ของเอกสารไทย

ถ้าเคยสังเกตและจำกันได้ เวลาสร้างเอกสารใน MS Word (โดยที่ยังไม่ได้กำหนดค่าปริยายใหม่) ฟอนต์เริ่มแรกที่โปรแกรมเลือกให้คือ Times New Roman ขนาด 11 พอยต์

เอามาพิมพ์ภาษาอังกฤษไม่มีปัญหา อ่านได้สบายตา แม้ว่ารูปร่างตัวอักษรจะเรียบๆ ตามขนบธรรมเนียมไปบ้าง

อย่างไรก็ตาม Times New Roman นี้เอามาพิมพ์ภาษาไทยไม่ได้ เพราะฟอนต์นี้ไม่มีภาพตัวอักษร (glyph) ภาษาไทยให้

ถึงตอนนี้ถ้าแก่พอ ก็คงจำได้ว่าต้องเปลี่ยนไปเลือกฟอนต์ Angsana UPC เพื่อพิมพ์คำไทย

แต่เนื่องจาก UPC มีแต่ภาพตัวอักษรไทยเท่านั้น เวลาเปลี่ยนก็ต้องเปลี่ยนแค่บริเวณที่เป็นภาษาไทย แถมยังต้องเพิ่มขนาดขึ้นมาเป็นอย่างต่ำ 14 พอยต์ด้วย ไม่ขนาดมันจะเล็กมากจนอ่านไม่ออก


ยุคนั้นเราจึงเจอความไม่สม่ำเสมอของตัวอักษรกันได้ง่ายๆ เช่น พิมพ์บทความภาษาไทยไปยาวๆ พอต้องแทรกคำที่เป็นภาษาอังกฤษ/เลขอาราบิก ภาพตัวอักษรพวกนั้นจะมีขนาดใหญ่เบิ้มขึ้นมาทันที

โชคดีที่ยุคถัดมามี Angsana New ให้ใช้ ซึ่งฟอนต์ตัวนี้มันมีภาพตัวอักษรทั้งไทยและอังกฤษให้ในตัว เวลาใช้ก็สบายใจหายห่วง ไม่ต้องมากังวลเรื่องปรับขนาดฟอนต์เมื่อเปลี่ยนภาษาอีก

แต่อย่างไรเสีย ขนาดที่ใช้อ่านง่ายสบายตาคือ 16 พอยต์ (มาตรฐานราชการไทยเลยหละ)



ถ้าใครลืมเลือนประวัติศาสตร์ตรงนี้กันไปแล้ว เวลาไปอ่านคำแนะนำการจัดย่อหน้าหนังสือภาษาอังกฤษ ก็จะตกใจเล็กน้อยว่า เฮ้ย! ฝรั่งนี่เค้าสายตาดีขนาดกำหนดฟอนต์มาตรฐานไว้ที่ 11-12 พอยต์เลยเหรอ?

คำตอบคือ 11-12 พอยต์ตัวอักษรอังกฤษ มันเทียบเคียงได้กับ 14-16 พอยต์ตัวอักษรไทยนั่นเอง

สันนิษฐานว่าตอนที่ภาพตัวอักษรภาษาไทยเริ่มถูกนำไปทำเป็นฟอนต์ เวลานับขนาดภาพตัวอักษร เค้านับตามแนวตั้งซึ่งรวมพวกสระบนล่างและวรรณยุกต์เข้าไปด้วย

ทำให้ขนาดของภาพตัวอักษร ก-ฮ มันโดนบีบลงไปนั่นเอง เวลาจะใช้งานจริงๆ เลยต้องเพิ่มขนาดฟอนต์โดยรวมทั้งหมด เพื่อให้ขนาดเฉพาะของภาพตัวอักษร ก-ฮ มันกลับมามีขนาดใกล้เคียงกับภาพตัวอักษร a-z



แต่ปัจจุบันนี้ หากเปลี่ยนไปใช้ Google Docs หรือ LaTeX พิมพ์งาน จะพบว่าฟอนต์ไทยในขนาดที่อ่านได้ มันคือขนาด 11-12 พอยต์เหมือนฟอนต์อังกฤษมาตั้งแต่แรก ไม่ต้องไปเพิ่มขนาดมันเป็น 14-16 พอยต์แล้ว

ก็หวังว่าระบบมาตรฐานฟอนต์ไทยขนาด 16 พอยต์ใน MS Word จะล้มหายตายจากไปเสียที แล้วไปใช้มาตรฐานฟอนต์ไทย 12 พอยต์เหมือนสากลโลกเค้า (เช่น มีฟอนต์ใหม่ Angsana Normalize มาแทนที่ Angsana New ไรงี้)

ระหว่างนี้ก็ทำได้แต่ระวังสับสนกันต่อไป ...

หรือถ้าจะให้แน่ใจ ตอนพิมพ์ลงกระดาษแล้ว ลองเอาไม้บรรทัดมาวัดความสูงของภาพตัวอักษร ก-ฮ ให้ได้ราวๆ 4.5 มิลลิเมตร ก็จะได้ว่าภาพตัวอักษรนั้นมีขนาด 16 พอยต์แบบพิมพ์บน MS Word หรือเทียบได้กับขนาด 12 พอยต์แบบพิมพ์บน Google Docs นั่นเองครับ

Jan 5, 2016

แมว


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

ตั้งแต่เราไปเรียนที่กรุงเทพ พอกลับมาบ้านแต่ละทีก็ไม่ค่อยได้เจอแมวเลย แม่เล่าว่าพ่อไม่ค่อยชอบที่แมวมานอนบนรถ สงสัยพอโดนไล่บ่อยๆ ก็กลัวที่จะมาเยี่ยมบ้านเราหละมั้ง

รูปภาพที่โพสต์โดย @neizod เมื่อ



เวลาผ่านไปหลายปี ทำให้เราแทบลืมว่าเคยมีกันและกันไปแล้ว กลับบ้านปีใหม่คราวนี้จริงๆ ก็เห็นแมวมาอยู่หน้าบ้านครั้งนึงนะ แต่ว่าวันนั้นเรารีบออกไปข้างนอก เลยไม่ได้อยู่เล่นด้วยเลย

เย็นวันนี้แมวโดนรถชนซะแล้ว

เราได้แต่สงสัยว่า ถ้าเมื่อคืนเราไม่จอดรถไว้หน้าบ้าน หรือถ้าวันนี้เราตื่นเช้าออกไปข้างนอก ตอนที่ไม่มีรถเราบังวิสัยทัศน์ในซอยแคบ คนขับรถคนเค้าจะมองเห็นแมวหรือเปล่า

ขอโทษนะ อาจจะพูดได้ไม่เต็มปากหรอกว่านี่เป็นความผิดเรา แต่เราก็รู้สึกมีส่วนผิดจริงๆ

หลับให้สบายนะเจ้าแมว ฉันจะไม่ลืมแกอีกแล้ว

Jan 1, 2016

สรุปปี 2014-2015

เพิ่งเห็นว่าลืมเขียนของปี 2014 ตอนแรกก็ว่าจะแยกโพสออกไปแล้วตั้งเวลาย้อนหลัง แต่พอไล่ลิสต์ดูแล้วปีนั้นก็ไม่ค่อยได้ทำอะไรเท่าไหร่ ไม่เป็นไรงั้นรวบทั้ง 2 ปีมาไว้ตรงนี้เลยละกัน จะได้ดูเหมือนได้ทำอะไรเยอะแยะ 555

  • ตีพิมพ์งานวิชาการนานาชาติ งานนี้ทำกับ @lewcpe และ @pruet ตีลง Springer โดยเสนอวิธีการทำให้รหัสผ่านแข็งแรงขึ้นแต่ก็ยังจดจำได้
  • สัมมนาวิชาการนานาชาติ ต่อเนื่องจากอันบนครับ .. โอเคแม้ว่างานสัมมนาจะจัดที่ไทย แต่ก็เป็นระดับภูมิภาคชาวต่างชาติมากันเต็ม แย่หน่อยที่ตอนนั้นยังขี้อายเลยทำให้ไม่ค่อยได้หาเพื่อนใหม่เท่าไหร่ (ถ้าไม่ได้อาจารย์ @dittaya ช่วยไว้คงแย่อะ)
  • เป็นอาจารย์พิเศษ เสียดายอย่างว่าก่อนวันสัมภาษณ์เกิดเหตุการณ์ไม่สงบทางการเมือง จนทำให้การจราจรติดขัดอดได้งานเลย
  • อ่านนิยายสถาบันสถาปนาของอาซิมอฟ รู้สึกว่าเป็นนิยายที่ดีเกือบๆ จะที่สุดเท่าที่เคยอ่านมาเลย
  • ไปฮ่องกง-มาเก๊าครั้งแรก อันฃนี้ไม่มีอะไรมาก เที่ยวขำๆ วันปีใหม่ ได้เห็นอะไรแปลกๆ เยอะดี ชอบสุดคือระบบขนส่งมวลชนที่โคตรจะสะดวกและรวดเร็ว
  • จบป.โท จบตรงตามเวลา น่าจะเป็นเพราะเรียนสายประยุกต์ด้วย งานวิจัยเลยไม่ได้ยากมากเท่าเพื่อนๆ ที่เรียนเพียว ทำให้ยังจบได้ตามกำหนด (ถ้าเรียนเพียวอาจจะช้ากว่านี้ 555)
  • เดินทางเยอะมาก เนื่องจากรับงานกับอาจารย์ @jittat ทำให้ได้ไปเที่ยวมหาวิทยาลัยเกษตรศาสตร์ครบทุกวิทยาเขตเลย พอกลับมาที่บ้านก็เจอพ่อกับแม่พาตะลุยขับรถเที่ยวไทยอีก ทำให้ช่วงนี้เมารถง่ายหน่อยนะ @_@
  • กลับมาสเก็ตช์รูปบ่อยเหมือนก่อนแล้ว หลังจากหันหน้าเข้าหาคีย์บอร์ดมาหลายปี พอได้ปากกาดีๆ มาใช้เลยทำให้มีอาการอยากวาดภาพขึ้นหลายเท่าตัว
  • ไม่ดองเกมแล้ว จริงๆ อันนี้ไม่ค่อยดีเท่าไหร่ (ในแง่เบียดบังเวลาที่ควรจริงจัง) แต่ก็ทำให้ตอนนี้พูดได้อย่างเต็มปากแล้วว่าไม่ได้ซื้อเกมมาดองไว้เฉยๆ นะ 555

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

  • ย้ายบล็อกไปไว้ที่อื่น ขั้นพื้นฐานขอให้แทรกโค้ดโปรแกรมกับสมการได้ง่ายๆ ตอนนี้ดูโปรเจค pagist ของ @dtinth ไว้อยู่ หวังว่าจะดัดแปลงเอามาใช้ได้ไม่ยากเย็นนะ
  • อ่านหนังสือให้จบเดือนละสองเล่ม ดูเหมือนเยอะนะ แต่เพราะว่านี่เป็นคนอ่านแล้วดอง (ตอนนี้ดองไว้ร่วมสิบเล่มแล้ว) อ่านไปได้ซัก 40% ไม่ว่าจะสนุกแค่ไหนจะเริ่มรู้สึกกลัวมันจบ แต่ถ้าพยายามอ่านไปจนซัก 75% จะกลับมามีแรงฮึดอ่านรวดเดียวจบละ
  • เขียนบล็อกให้ได้เดือนละครั้ง อันนี้พังพินาศมานานแล้ว ต้องขยันๆ หน่อย ปัญหาสำคัญคือมีเรื่องที่อยากเขียนแล้วแต่ก็รู้สึกว่ามันสั้นไป (ปัจจุบันมีบล็อกดองอยู่เป็นร้อยตอนแล้ว) ควรจะลดความคาดหวังลงว่าเรื่องที่เอามาเขียนต้องคิดตกตะกอนมาจนสมบูรณ์แบบก่อน คือเขียนๆ ไปเลยถ้าจะมีรายละเอียดที่ผิดนิดผิดหน่อยก็ช่างมัน ตอนนี้พยายามให้ใจความสำคัญชัดเจนไว้เป็นพอ
  • เขียนหนังสือให้เสร็จ อันนี้ก็พังพินาศมานานแล้วเหมือนกัน ยังรอดอยู่ได้ไม่โดน @markpeak เอาไม้ฟาดหัวก็ดีแค่ไหนแล้ว T^T
  • เริ่มมองหางานประจำ/ที่เรียนต่อ โอเคว่ามันต้องเลือกเอาซักอย่าง ภายในครึ่งปีนี้ก็ควรไปนั่งคิดดูดีๆ ว่าจะไปเส้นทางไหนกันแน่
  • ลดน้ำหนัก ปีก่อนๆ มันกลับมาขึ้นอีกแล้ว ว่าจะพยายามดันมันลงไปใหม่อีกที หลังจากกินดีอยู่ดีมาพักใหญ่

ลองดูแค่นี้ก่อน ไม่แน่ใจว่าเยอะเกินไปหรือยัง กลัวว่าพอตั้งเป้าเยอะมากเกินไปเดี๋ยวจะรู้สึก overwhelm แล้วก็พังพินาศทุกอันอีก

เจอกันใหม่ในอีกครึ่งปีครับ

Dec 29, 2015

เจองานจับฉลากแลกของขวัญที่มีคนได้ของตัวเอง ไม่ต้องตกใจไปโอกาสมีสูงเกินครึ่ง

วันก่อนเห็นเพื่อนแลกของขวัญปีใหม่แล้วดันจับได้ของตัวเองก็ฮาดี มองเผินๆ เหมือนว่าเรื่องแบบนี้จะเกิดขึ้นได้ยาก แต่เท่าที่เคยเล่นแลกของขวัญมา เรื่องประมาณนี้เกิดขึ้นบ่อยกว่าที่คิดนะ เลยลองมานั่งโซ้วสมการความน่าจะเป็นดู ว่าโอกาสที่ในงานเลี้ยงหนึ่งๆ จะมีคนจับของขวัญได้ของตัวเองเป็นเท่าไหร่

แต่ก่อนอื่นต้องเข้าใจกฎการแลกของขวัญ ซึ่งมีขั้นตอนประมาณนี้
  1. ทุกคนซื้อของขวัญมาร่วมสนุกในงานคนละอย่าง โดยเอาของขวัญใส่กล่องไว้ไม่ให้ใครรู้ว่าด้านในเป็นอะไร
  2. พอแต่ละคนไปถึงงาน คนจัดงานจะแปะป้ายชื่อเจ้าของของขวัญไว้กับกล่อง พร้อมหย่อนฉลากรายชื่อของคนนั้นลงไห
  3. เมื่อถึงเวลาแลกของขวัญ ให้ประธานในพิธีหรือใครซักคนที่ใหญ่ที่สุดในงาน เป็นคนเริ่มจับฉลากรายชื่อจากไหเป็นคนแรก
  4. ประธานจับได้ชื่อใครก็เอากล่องของขวัญของคนนั้นไป แล้วก็ให้คนที่โดนจับชื่อเมื่อกี้ เป็นคนเริ่มจับฉลากในไหวนต่อไปเรื่อยๆ
  5. ถ้าเกิดมีคนจับได้ชื่อประธาน จะถือว่าเป็นการแลกของขวัญที่ครบรอบวง ก็ให้คนนั้นเอาของขวัญของประธานไป แล้วผู้จัดงานเลือกใครซักคนจากคนที่ยังไม่ได้จับของขวัญ มาเป็นหัวจับของขวัญรอบต่อไป
  6. การแลกของขวัญจะสิ้นสุดลงเมื่อไม่มีฉลากรายชื่อเหลือในไห
จากกฎนี้เห็นได้ชัดว่า เมื่อถึงตอนใกล้จบการแลกของขวัญที่มีฉลากรายชื่อในไหเหลือแค่ 2 ใบ ใบนึงจะเป็นของคนแรกและอีกใบเป็นของคนที่ยังไม่ได้จับฉลาก ดังนั้นโอกาสที่คนจับก่อนจะจับฉลากได้ชื่อคนแรกและบังคับให้คนสุดท้ายจับชื่อตัวเอง โอกาสนี้จะสูงถึง 50% เลย

ยกตัวอย่างให้ชัดขึ้นไปอีก สมมตินายประธานเป็นคนจับฉลากคนแรก แล้วก็มีคนจับตามๆ กันมาอีกหลายคน จนตอนสุดท้ายเหลือคนที่ยังไม่ได้จับฉลาก 2 คนคือสมศักดิ์กับมานี ถ้าคนก่อนหน้าจับฉลากได้ชื่อสมศักดิ์ แปลว่าสมศักดิ์จะเป็นคนจับฉลากคนต่อไป แต่รายชื่อในไหตอนนี้จะเหลือแค่มานีกับประธานเท่านั้น ที่โอกาสครึ่งๆ ถ้าสมศักดิ์จับได้ชื่อประธาน ก็จะเหลือคนไม่ได้จับฉลากคือมานี และฉลากที่เหลือในไหก็เป็นชื่อมานี นั่นก็คือมานีจะต้องจับฉลากได้ของขวัญของตัวเองแน่นอน

โอกาสที่ในงานแลกของขวัญงานหนึ่ง จะมีคนได้ของขวัญเป็นของตัวเองสูงถึง 50% ซึ่งจริงๆ แล้วโอกาสน่าจะมากกว่านี้อีกนิดหน่อยด้วยซ้ำ เพราะยังไม่ได้คิดโอกาสที่คนกลางๆ จะจับได้ของตัวเองทันทีหลังจากเกิดการจับฉลากครบรอบวงเลย



คิดได้แค่นี้ก็พอใจแล้ว แต่ก็โดน @NungNing ถามต่อว่า ถ้าไม่จับฉลากกันต่อไปเรื่อยๆ แบบนี้หละ แต่เปลี่ยนเป็นให้ทุกคนหยิบฉลากจากไหพร้อมกันเลย แล้วค่อยเปิดดูทีเดียวว่าใครได้ของขวัญจากใคร ยังจะมีโอกาสสูงอยู่มั้ยที่จะมีคนจับฉลากได้ของขวัญของตัวเอง?

ตอนนั้นหมดพลังแล้ว ให้คิดต่ออย่างเดียวคงไม่ออกแน่ๆ เลยเขียนโปรแกรมง่ายๆ มาดูค่าเอาเลยนั่นแหละ
#!/usr/bin/env python3
from random import shuffle
from collections import Counter

shuffled = lambda ls: shuffle(ls) or ls
own_gift = lambda n: [i for i, j in enumerate(shuffled(list(range(n)))) if i == j]
สมมติให้มีคนแลกของขวัญกัน 10 คน ทดลองไปล้านครั้ง พบว่ามีประมาณสามแสนหกหมื่นครั้งเท่านั้น (36%) ที่การแลกของขวัญนั้นจะไม่มีคนได้ของตัวเอง

ตอนนั้นก็ตกใจว่าเลข 36% มาจากไหน นึกว่าโอกาสไม่ได้ของตัวเองมันจะมีค่าสูงกว่า 50% ที่คิดได้จากการแลกของขวัญวนไปเรื่อยๆ ซะอีก นี่กลายเป็นว่าโอกาสไม่ได้ของตัวเองมันยิ่งเหลือน้อย เลยทดลองเพิ่มโดยนับแยกว่า ในแต่ละการทดลองแลกของขวัญนั้น มีคนกี่คนที่ได้ของขวัญของตัวเอง

ทดลองแลกของขวัญกัน 10 คนล้านครั้งเหมือนเดิม แล้วก็ตกใจเพิ่มกับหน้าตาผลลัพธ์ที่ไม่โกหกดังนี้

จำนวนคนที่จับได้ของขวัญของตัวเอง จำนวนครั้งของเหตุการณ์ที่เกิด ข้อคาดการณ์โอกาสการเกิด
0 368,243 1/0! x 1/e
1 367,640 1/1! x 1/e
2 183,800 1/2! x 1/e
3 61,406 1/3! x 1/e
4 15,269 1/4! x 1/e
5 3,014 1/5! x 1/e
6 545 1/6! x 1/e
7 67 1/7! x 1/e
8 16 1/8! x 1/e

ถึงตอนนี้ก็พอจะเข้าใจแล้วว่ามันเกิดอะไรขึ้น



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

เริ่มจากกรณีที่ไม่มีใครจับของขวัญได้ของตัวเองก่อน จะได้ว่า
  • คนแรกมีโอกาสจะจับไม่ได้ของตัวเองที่ (n-1)/n
  • คนที่สองมีโอกาสที่จะจับไม่ได้ของตัวเอง แบ่งได้ 2 แบบ คือ
    • คนแรกจับได้ของคนที่สองไปแล้ว (โอกาส 1/n) ดังนั้นคนที่สองจับไม่ได้ของตัวเองแน่ๆ (โอกาส 1) รวมโอกาสได้เป็น 1/n
    • คนแรกจับไม่ได้ของคนที่สอง (โอกาส (n-1)/n) คนที่สองต้องจับหลบให้ไม่ได้ของตัวเอง (โอกาส (n-2)/(n-1)) รวมโอกาสได้เป็น ((n-1)/n)((n-2)/(n-1))
    • สรุปว่าโอกาสทั้งหมดที่คนที่สองจะจับไม่ได้ของตัวเองคือ 1/n + ((n-1)/n)((n-2)/(n-1))
  • โอกาสคนที่สามจะคล้ายๆ กับของคนที่สอง คือ 2/n + ((n-2)/n)((n-3)/(n-2))
  • คนที่ i มีโอกาส (i-1)/n + ((n-i-1)/n)((n-i)/(n-i-1)) ที่จะจับไม่ได้ของตัวเอง

เนื่องจากเหตุการณ์ทั้งหมด ต้องเกิดขึ้นพร้อมกัน ดังนั้นสรุปได้ว่า
P(n คนไม่มีใครได้ของตัวเอง) = (n-1)/n x (1/n + ((n-1)/n)((n-2)/(n-1))
                                  x ... x ((i-1)/n + ((n-i-1)/n)((n-i)/(n-i-1))
                                  x ... x ((n-n)/n + 0)
                        = (n-1)/n x (1/n + (n-2)/n) x .... x ((i-1)/n + (n-i)/n) x ... x 1
                        = Product  (n-1)/n  for integer i from 1 to n-1
                        = Product  1 - 1/n  for integer i from 1 to n-1
                        = (1-1/n)^(n-1)
จากสมการข้างต้น ถ้าลองแทนตัวเลขน้อยๆ เช่น n=1,2,3,4 เข้าไป จะได้คำตอบเป็น 1, 0.5, 0.445, 0.422 ตามลำดับ แต่เมื่อลองแทนเลขมากๆ แล้ว ผลลัพธ์จะลู่เข้าหาค่าเดียวกันคือ 0.367879... นั่นก็เพราะ
limit  (1-1/n)^(n-1)  when n -> infinity = limit  (1-1/n)^n  when n -> infinity
                                         = limit  ((n-1)/n)^n  when n -> infinity
                                         = limit  (n/(n+1))^n  when n -> infinity
                                         = limit  1/((n+1)/n)^n  when n -> infinity
                                         = 1/(limit  ((n+1)/n)^n  when n -> infinity)
                                         = 1/(limit  (1+1/n)^n  when n -> infinity)
                                         = 1/e
สวยมั้ย อยู่ดีๆ ก็ได้ค่า e ออกมาเฉยเลย

ส่วนการพิสูจน์กรณีมีคนได้ของขวัญของตัวเองแค่คนเดียว จะมีฟอร์มที่ต่างไปเล็กน้อยเนื่องจากเหตุการณ์จะไม่เกิดขึ้นต่อเนื่องกันแล้ว เขียนสมการออกมาได้เป็น
P(n คนมี 1 คนได้ของตัวเอง) = (1/n)P(n-1 คนไม่มีใครได้ของตัวเอง)
                              + ((n-1)/n)(1/(n-1))P(n-2 คนไม่มีใครได้ของตัวเอง)
                              + ...
                              + ((n-(n-1))/n)(1/(n-(n-1)))P(n-n คนไม่มีใครได้ของตัวเอง)
                        = (1/n)P(n-1 คนไม่มีใครได้ของตัวเอง)
                              + (1/n)P(n-2 คนไม่มีใครได้ของตัวเอง)
                              + ...
                              + (1/n)P(n-n คนไม่มีใครได้ของตัวเอง)
                        = (1/n)(Sum  P(n-i คนไม่มีใครได้ของตัวเอง)  for integer i from 1 to n)
สมมติว่ามีคนมาร่วมงานแลกของขวัญเยอะ อาจจะมองว่า P(n-i คนที่ไม่มีใครได้ของตัวเอง) มีค่าไม่แตกต่างกันสำหรับแต่ละค่า i เลยก็ได้ ทำให้เราสามารถยุบผลรวมยุ่งๆ ด้านหลัง ให้เหลือแค่เอาความน่าจะเป็นไปคูณด้วย n ก็พอ ซึ่งมันก็จะโดนหักล้างทิ้งไปกับ (1/n) ด้านหน้าทันที

ดังนั้นจะได้ว่า ค่าความน่าจะเป็นของงานเลี้ยงแลกของขวัญที่จะมีคนหนึ่งคนได้จับได้ของตัวเอง เป็น 1/e เท่ากันกับกรณีไม่มีใครจับได้ของตัวเองเลย

สำหรับกรณีที่มีคนสองคนจับได้ของตัวเอง จะเริ่มพิสูจน์ยุ่งยากมากขึ้นไปอีก แต่ใจความจะคล้ายๆ กับการพิสูจน์คนเดียวได้ของตัวเอง ซึ่งได้แก่การมองแยกกรณี โดยแบบที่หนึ่งจะให้คนแรกจับได้ของตัวเองไปแล้ว หลังจากนั้นจึงหาโอกาสของคนที่เหลือที่จะมีหนึ่งคนจับได้ของตัวเองเป็นเท่าไหร่ กับแบบที่สองที่ให้คนแรกจับไม่ได้ของตัวเอง แล้วหาโอกาสของคนที่เหลือที่จะมีสองคนจับได้ของตัวเองเป็นเท่าไหร่ (ถ้าคิดด้วยวิธีนี้ไม่ผิด จะได้หน้าตาสมการเป็นการหาผลรวมของเทอม ((n-k)/n)((n-k+1)/(n-1))((n-k+2)/(n-2))...(1/(n-k+1))P(n-k คนมี 1 คนได้ของตัวเอง))

กรณีอื่นๆ ที่เหลือเนื่องด้วยพื้นที่กระดาษไม่พอเขี.... เฮ้ย ไม่ใช่แฟร์มาต์! จริงๆ แล้วต้องบอกว่าการพิสูจน์ด้วยท่านี้มันเริ่มยุ่งยากซับซ้อนจนไปต่อไม่ไหวแล้ว แต่ถ้ายังจำกันได้ นี่มันการแจกแจงปัวซองที่มีค่า λ=1 ชัดๆ ก็เอาเป็นว่าใครอยากพิสูจน์ต่อก็ลองใช้ท่านี้ดูนะ อาจจะได้บทพิสูจน์ที่สั้นและสวยงามกว่าด้วย



เห็นแล้วว่าการสุ่มจับฉลากทั้งหมดทีเดียวพร้อมกันมันไม่เวิร์ค กลับไปดูวิธีดั้งเดิมที่ค่อยๆ ให้จับฉลากทีละคน จับได้ชื่อใครก็ให้คนนั้นไปจับฉลากต่อ วิธีนี้เขียนเป็นโค้ดออกมาได้ว่า
from random import randint

def exchange(ls):
    owned = [None for _ in ls]
    current = 0
    while ls:
        if owned[current] is not None:
            current = owned.index(None)
        gone = randint(0, len(ls)-1)
        owned[current] = ls[gone]
        current = ls[gone]
        del ls[gone]
    return owned
เอาฟังก์ชัน exchange ไปแทนที่ฟังก์ชัน shuffled ข้างบนแล้วทดลองใหม่ ก็ได้ผลลัพธ์ที่ไม่ต่างไปจากตารางแรกซักเท่าไหร่เลย



ดังนั้นไม่ว่าจะให้จับฉลากพร้อมกันหมดในทีเดียว หรือจะค่อยๆ จับทีละคน พอจับได้ชื่อใครก็ให้คนนั้นจับต่อ ทั้งสองวิธีนี้ยังไงก็จะมีคนซวยโชคดีอย่างน้อยหนึ่งคน ที่จับได้ของขวัญของตัวเองด้วยโอกาสสูงเท่ากันที่ 1-1/e หรือประมาณ 63.21% ครับ โดยที่(แทบ)ไม่ต้องสนใจด้วยว่ามีคนมาแลกของขวัญกันกี่คน

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

สวัสดีปีใหม่ 2016 ล่วงหน้าครับ

Aug 13, 2015

จักรวาลวิทยาและความตาย: YOLO

YOLO เป็นสแลงของวัยรุ่นในแถบประเทศที่พูดภาษาอังกฤษ มันย่อมาจากประโยคที่บอกว่า you only live once ที่แปลว่า "คุณมีชีวิตอยู่แค่ครั้งเดียว" ดังนั้นจะทำอะไรก็ทำไปเถอะ น่าเสียดายว่าหลายคนเอาไปใช้ในทางที่สุดโต่งอย่างการทำผิดกฎหมาย ทำให้คำนี้โดนมองในแง่ลบอย่างหนัก ต่างไปจากวลี carpe diem ในภาษาละตินที่แปลว่า "จงฉกฉวยชั่นขณะนี้ไว้" มันเป็นคำประพันธ์โบราณโดย Horace เมื่อกว่าสองพันปีก่อน และได้รับการเผยแพร่ซ้ำผ่านหนังขึ้นหิ้งอย่าง Dead Poet Society ทำให้วลีนี้ดูสุขุมนุ่มลึกกว่าคำข้างต้นที่เป็นได้เพียงสบถของพวกนอกคอก


แต่บทความนี้ไม่ได้มาเจาะลึกเรื่องทางประวัติศาสตร์ สังคม หรือแม้กระทั่งความหมายของคำนั้นหรอกครับ

เพราะสิ่งที่น่าสนใจยิ่งกว่า คือแนวคิดที่แฝงไว้ว่า "ชีวิตนี้มีแค่ครั้งเดียว"

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

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

พูดง่ายๆ ว่าวิญญาณนั้นอยู่ในมิติอื่นที่เราไม่สามารถสังเกตได้นั่นเอง

และแน่นอนว่า เมื่อไหร่ก็ตามที่วิทยาศาสตร์จะพยายามสังเกตวิญญาณให้ได้ ศาสนาก็จะหลบเลี่ยงไปอธิบายวิญญาณในรูปแบบอื่นแทน ดังนั้นบทความนี้จะยังไม่มาเล่นวิ่งไล่จับกัน แต่จะฟันธงไปก่อนเลยว่าวิญญาณนั้นไม่มี เพื่อที่จะได้สำรวจความคิดที่งอกเงยขึ้นมาอย่างเต็มที่

เมื่อเราปฏิเสธเรื่องวิญญาณไปแล้ว จะเห็นได้ว่าส่วนที่เหลือก็ง่ายทันที ความรู้สึกนึกคิด ความฝัน ความตระหนักในตัวตน (self-awareness) ต่างเป็นกิจกรรมที่เกิดขึ้นในสมองทั้งนั้น เมื่อสมองหยุดทำงานลง ก็หมายถึงความตายที่เราคุ้นเคยกันดี

แต่ความตายนั้นเป็นจุดจบจริงหรือ?

แบบจำลองจักรวาลที่ได้รับการยอมรับอย่างมากที่สุด ณ ขณะนี้ เสนอว่าจักรวาลนั้นมีจุดกำเนิด (big bang) เมื่อสิบสี่พันล้านปีที่แล้ว และขยายตัวมาเรื่อยๆ ด้วยอัตราเร่งที่สูงอย่างน่าใจหายถึงทุกวันนี้

หากจักรวาลยังคงขยายตัวเช่นนี้ต่อไป แรงโน้มถ่วงจะเป็นฝ่ายพ่ายแพ้ ท้ายที่สุดแล้วจักรวาลจะเย็นตัวลงเพราะไม่เหลือแหล่งพลังงานอีกต่อไป ดาวทั้งหลายดับสิ้นไปจนมีแต่ความมืดมิดปกคลุมทุกหนแห่ง ช่วงเวลาดังกล่าวจะยาวนานตราบชั่วนิจนิรันดร์และไม่มีวันย้อนกลับ

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

Aug 6, 2015

ตรวจ 2^n

วันก่อนเห็นทวีตนี้ของ @Methuz


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




ถ้าไม่เชี่ยววิเคราะห์อัลกอริทึม ดูแค่นี้อาจยังมองไม่เห็นว่าปรับปรุงดีขึ้นจริงหรือเปล่า เลย compile เป็น assembly ออกมาแล้วไล่ดูทีละบรรทัดเลย

คำสั่งที่ใช้ compile ให้เป็น assembly (GAS syntax) คือ

$ gcc -S -O3 program.c

อันนี้คือผลลัพธ์แบบแรกที่คุณ @Methuz เสนอ (ตัดมาเฉพาะส่วนฟังก์ชันที่สำคัญนะครับ)

chkpowof2:
        xorl    %eax, %eax
        testl   %edi, %edi
        je      .L42
        movl    %edi, %eax
        negl    %eax
        andl    %edi, %eax
        cmpl    %eax, %edi
        sete    %al
        movzbl  %al, %eax
.L42:
        rep ret

และอันนี้เป็นแบบที่ปรับปรุงแล้ว

chkpowof2:
        xorl    %eax, %eax
        testl   %edi, %edi
        je      .L42
        leal    -1(%rdi), %eax
        testl   %edi, %eax
        sete    %al
        movzbl  %al, %eax
.L42:
        rep ret

โอเค ใครอ่านถึงตรงนี้แล้วไม่งงก็กดปิดได้ เพราะเห็นชัดว่าปรับปรุงให้ดีขึ้นจริง

ส่วนใครงงอยู่ เดี๋ยวมาไล่โค้ดกันทีละบรรทัดกันครับ :D



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

ในฟังก์ชันที่ตัดมานี้มี register ที่สำคัญอยู่ 2 ตัว คือ %eax และ %edi และยังมี register ตัวอื่นอีก เช่น %al ซึ่งเป็นส่วนย่อยของ %eax โดยอธิบายความสัมพันธ์ได้ดังแผนภาพนี้

sample value:
0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111

a family register:
                                                            |- %ah -| |- %al -|
                                                            |------ %ax ------|
                                        |--------------- %eax ----------------|
|----------------------------------- %rax ------------------------------------|

di family register:
                                                            |------ %di ------|
                                        |--------------- %edi ----------------|
|----------------------------------- %rdi ------------------------------------|

ดังนั้นหากค่าของ %eax (32 บิต) ถูกตั้งไว้เป็น 0x89ABCDEF เมื่อเรียกดูค่าของ %al (8 บิต) ก็จะว่ามีค่าเป็น 0xEF

ส่วนหน้าที่พิเศษของ register ทั้ง 2 ตัวในที่นี้คือ
  • %eax เก็บค่าที่จะ return จากฟังก์ชัน
  • %edi เก็บค่า argument ของฟังก์ชัน

พอคุ้นเคยกับ register เหล่านี้แล้ว คราวนี้ก็ได้เวลามาดูโค้ดจริงๆ เสียที

บรรทัด 1

xorl    %eax, %eax

บรรทัดแรกนี้สั่งล้างค่า %eax ให้เป็นศูนย์ไว้ก่อน โดยใช้เทคนิคว่า ค่าใดๆ เมื่อนำไป xor กับตัวเองแล้วจะได้ศูนย์เสมอ ท่านี้จะเร็วกว่าการโหลดข้อมูลจาก memory มาถม

บรรทัด 2-3:

testl   %edi, %edi
je      .L42

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

บรรทัด 4-8 ของแบบแรก:

movl    %edi, %eax
negl    %eax
andl    %edi, %eax
cmpl    %eax, %edi
sete    %al

บรรทัดต่อๆ มาเป็นชุดคำสั่งในส่วนหลังที่ถามว่า หากมอง x เป็น binary แล้ว จะมีตัวเลข 1 เพียงตัวเดียวเท่านั้นหรือเปล่า? ซึ่งโค้ดชุดนี้ทำงานโดยเริ่มจากการคัดลอกค่า x จาก %edi มาไว้ที่ %eax ก่อน แล้วหาค่าติดลบแบบ two complement ของ %eax และเก็บไว้ที่เดิม (มีค่าเทียบได้กับการคำนวณ ~x+1) แล้วจึงเอาค่าของ %edi ไปทำการ and และเก็บค่าไว้ใน %eax (ตอนนี้ %eax ก็จะเก็บค่าของ x&(~x+1) แล้ว) สุดท้ายถามว่า %eax ลบ %edi ได้ศูนย์หรือเปล่า ถ้าใช่ก็จะตั้งค่า %al ให้เท่ากับหนึ่ง (ซึ่งก็คือ ฟังก์ชันจะคืนค่า true)

บรรทัด 4-6 ของแบบสอง:

leal    -1(%rdi), %eax
testl   %edi, %eax
sete    %al

ชุดบรรทัดพวกนี้มีหน้าที่และผลลัพธ์เหมือนกับชุดบรรทัดในหัวข้อย่อยก่อนเลยครับ สิ่งที่แตกต่างคือวิธีการเท่านั้น โดยโค้ดเริ่มทำงานจากคัดลอกค่า x จาก %rdi มาลบหนึ่งระหว่างทางแล้วเอาผลลัพธ์ไปเก็บไว้ใน %eax (ตรงนี้เนื่องจากมีการคำนวณพื้นฐานอย่างบวกลบ ทำให้ compiler รวบคำสั่งเข้ามาที่เดียว ต่างจากการคำนวณซับซ้อนอย่างการหาค่าติดลบครับ) เรียบร้อยแล้วเอา %edi มา and กับ %eax หากได้ศูนย์ก็จะตั้งค่า %al ให้เท่ากับหนึ่ง (คืน true)

บรรทัดรองสุดท้าย

movzbl  %al, %eax

ถึงตอนนี้การคำนวณหลักๆ ได้เสร็จสิ้นหมดแล้ว ที่ยังไม่เสร็จคือเตรียมค่าที่จะส่งคืน เพราะก่อนหน้านี้เราใช้ %al ยาว 8 บิต แต่ตอนส่งค่าคืนต้องใช้ %eax ที่ยาว 32 บิต บรรทัดนี้จะช่วยแปลงค่าดังกล่าว

บรรทัดสุดท้าย

rep ret

สุดท้ายก็ได้เวลาส่งคืนคำตอบครับ สังเกตว่า compiler เพิ่มความมั่นใจให้คำสั่งนี้ด้วยการเติม rep ไว้ข้างหน้า เพื่อให้โค้ดที่ทำงานมาถึงบรรทัดนี้ทำคำสั่ง ret ซ้ำๆ จนกว่าจะสำเร็จนั่นเอง



สุดท้ายการวิเคราะห์อัลกอริทึม/โค้ดยังไม่พอ นี่คือผลเชิงประจักษ์ของเวลาที่ใช้ไปสำหรับการตรวจว่าเลขดังกล่าวเป็น 2^n หรือไม่ สำหรับจำนวนเต็มบวกทุกตัวที่มีค่าต่ำกว่าสองพันล้านครับ

$ gcc --version
gcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2
$ cat /proc/cpuinfo
...
model name : Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz
...


$ time method-1         # ((x != 0) && ((x & (~x + 1)) == x))
real 0m0.234s ~ 0m0.289s
user 0m0.232s ~ 0m0.288s
sys 0m0.000s


$ time method-2         # ((x != 0) && ((x & (x - 1)) == 0))
real 0m0.212s ~ 0m0.262s
user 0m0.208s ~ 0m0.260s
sys 0m0.000s

ก็เร็วต่างกันประมาณ 10%


Jul 10, 2015

จำลอง WYSIWYG ตามแบบคนจนบน HTML5

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

ถ้าใช้เว็บสำเร็จรูปทั่วไปก็อาจจะไม่ยากเท่าไหร่ เช่น WordPress ที่มี WYSIWYG มาให้แต่เริ่มเลย หรือหากไม่ถูกใจก็ยังมีส่วนเสริม WYSIWYG ให้เลือกติดตั้งเพิ่มได้อีก

แต่ถ้าเริ่มทำเว็บเองจากศูนย์ หรือว่า framework ที่ใช้ไม่ดังพอจนไม่มีใครทำส่วนเสริมนี้ให้หละ? จะพบว่า requirement นี่เป็นสิ่งที่สร้างเองได้ยากมากๆๆๆ

ทางแก้สำหรับเหล่า geek คงเป็นการ "เลิกฝัน" แล้วกลับไปลุยดะกับโค้ดจัดย่อหน้าเลย (ซึ่งก็ไม่จำเป็นต้องยุ่งกับ HTML ตรงๆ อาจเลือกพวกที่เจ็บปวดน้อยลงอย่าง Markdown ก็ได้)

แต่ถ้าแนะนำแบบนี้กับคนทั่วไปที่ชินกับ MS Word คงไม่เวิร์กแน่

จริงๆ ทางออกสำหรับพวกเขาเหล่านั่น อาจไม่ใช่การแก้ไขทุกรายละเอียดผ่านกล่องข้อความในหน้าเว็บก็ได้

เพราะในเมื่อพวกเขาชินกับการใช้โปรแกรม Word อยู่แล้ว ก็ให้แก้ไขอะไรให้เรียบร้อยบน Word ไปเลย เรียบร้อยแล้วลากคลุมสิ่งที่ต้องการ Ctrl+C Ctrl+V มาวางบนกล่องข้อความหน้าเว็บก็พอ

ส่วนกล่องข้อความก็เปลี่ยนจาก <textarea> ไปเป็น <div contenteditable> แทน เท่านี้ก็เรียบร้อย (แล้วไปหาวิธีเซฟ/โหลดข้อความที่ <div> แทน ซึ่งไม่ยุ่งยากหรอกในโลกที่มี jQuery ให้ใช้ :P)



อยากลองเล่น? ขั้นแรกให้พิมพ์ตามนี้ลงไปใน address bar ของเว็บเบราเซอร์เพื่อสร้าง WYSIWYG ก่อนครับ

data:text/html, <html contenteditable>

ขั้นต่อมาก็ไปเปิดเอกสาร Word ซักอัน (หรือจะสร้างใหม่ก็ได้) แล้วลองคัดลอกข้อความมาวางบนหน้าเว็บนั้นดู

ส่วนอะไรที่เวิร์กไม่เวิร์กบ้าง เท่าที่ลองคร่าวๆ ได้ผลตามตารางนี้ครับ

การจัดรูปแบบ แท็กที่ใช้แทน MS Word LibreOffice Google Docs
Title <p align="center"><font size="6">
Subtitle <p align="center"><font size="5">
Heading 1-3 <h1> - <h3>
Heading <font size="4">
Quotations <blockquote>
Align Left / Center / Right / Justify <p align="left / center / right / justify">
Bold, Italic, Underline, Strikethrough <b> / <i> / <u> / <strike>
Superscript, Subscript <sup> / <sub>
Table <table><tbody><tr><td>
Bullet List <ul><li>
Numbering <ol><li>
Image <img src="data:image/png;base64,...">
Formula <img src="data:image/png;base64,...">

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

Jun 17, 2015

AutoHotKey กับสคริปต์เปลี่ยนภาษา

ลง Windows ใหม่อีกรอบและทำโปรแกรมเปลี่ยนภาษาด้วยปุ่ม CapsLock หายไปแล้ว เลยมาจดไว้ก่อน เผื่อเจอเหตุการณ์ประมาณนี้อีกจะได้ลดเวลาลองผิดลองถูก

  1. เช็คให้แน่ใจว่าการเปลี่ยนภาษาบน Windows ใช้ปุ่ม Alt+Shift เสียก่อน
  2. ดาวน์โหลดและติดตั้งโปรแกรม AutoHotKey
  3. กด Win+R พิมพ์ shell:startup มันจะเปิดแฟ้มเก็บโปรแกรมที่ถูกเรียกอัตโนมัติตอนเปิดเครื่องมาให้
  4. คลิกขวาเลือกสร้างสคริปต์ AutoHotKey (อย่าสร้างไฟล์เปล่าๆ เพราะจะไม่ได้ค่าปริยายมาด้วย) แล้วเพิ่มคำสั่งนี้ต่อท้ายไฟล์
    Capslock:: Send {Alt Down}{Shift}{Alt Up}
    

เรื่องประหลาดคือ ในสคริปต์ถ้าสลับตำแหน่งเป็นกด Shift ค้างไว้ก่อนแล้วค่อยกด Alt มันก็เปลี่ยนภาษาได้นะ แต่พอเปลี่ยนเสร็จเคอร์เซอร์จะหลุดโฟกัสไปเลย ไม่รู้ว่าเกิดอะไรขึ้น :\

May 25, 2015

ลองใช้ Lamy ครั้งแรก

ชีวิตนี้ไม่เคยคิดว่าจะใช้ปากกาด้ามแพงเกินร้อยบาทครับ แต่ก็ได้ Lamy มาฟรีด้ามนึงเป็นของขวัญวันรับปริญญา

ดองไว้เป็นปีแล้วเพราะไม่รู้ว่าใช้ยังไง จนกระทั่งไม่กี่วันก่อนที่ปากกาทุกด้ามจงใจหมึกตัน เลยได้ฤกษ์เอาออกมาลองใช้ซะเลย

ด้ามที่ได้มานี้หัวเป็นขนาด M ครับ โดยรวมแล้วเอาไปเขียนลื่นสนุกดี แม้มีสะดุดบางจังหวะ

แต่ตอนเอามาวาดรูปนี่รู้สึกควบคุมน้ำหนักเส้นไม่ได้เลย (ไม่เหมือนหัวลูกลื่นที่สามารถฝนบางๆ น้อยกว่าขนาดที่บอกไว้ได้) ...





สรุปแล้วไม่ค่อยชอบเท่าไหร่แฮะ นี่ผมแปลกไปเองหรือเปล่าเนี่ย?

May 22, 2015

Voronoi จากภาพ

วันก่อนไปเจอคำถามใน Code Golf มา เค้าให้สร้าง Voronoi เพื่อเลียนแบบภาพต้นฉบับครับ

อ่านแล้วสนุกดี ผลงานของคนอื่นที่ได้ๆ ก็ดูเหมือนงานศิลป์พวก Divisionism/Pointillism ด้วย เลยลองกลับมาเขียนเองแบบมั่วๆ บ้าง 555

import random
from PIL import Image
from PIL.ImageFilter import FIND_EDGES, GaussianBlur, SHARPEN

def coordinate(width, height):
    yield from ((x, y) for y in range(height) for x in range(width))

def normalized(choices):
    lower = min(weight for _, weight in choices)
    upper = max(weight for _, weight in choices)
    norm = lambda weight: (weight-lower) + (upper-lower)//4
    return [((x, y), norm(weight)) for (x, y), weight in choices]

def random_by_weight(choices):
    rand_val = random.uniform(0, sum(weight for _, weight in choices))
    index = 0
    count = 0
    while count < rand_val:
        count += choices[index][1]
        index += 1
    return choices.pop(index-1)[0]

def init_centroids(image, cells):
    width, height = image.size
    edge_img = image.filter(FIND_EDGES).filter(GaussianBlur).filter(SHARPEN)
    weight = lambda x, y: 256 - max(edge_img.getpixel((x, y)))
    choices = [((x, y), weight(x, y)) for x, y in coordinate(width, height)]
    return [random_by_weight(normalized(choices)) for _ in range(cells)]

def init_rgbs(image, centroids):
    rgb_im = image.convert('RGB')
    return [rgb_im.getpixel((x, y)) for x, y in centroids]

def simulate_voronoi(image_path, cells=25, scale=None):
    image = Image.open(image_path)
    if scale is not None:
        image.thumbnail((scale, scale), Image.ANTIALIAS)
    centroids = init_centroids(image, cells)
    rgbs = init_rgbs(image, centroids)
    return image.size, list(zip(centroids, rgbs))
ผลลัพธ์ที่ออกมาก็ประมาณนี้ครับ (ที่ 500 เซลล์ Voronoi)

เอาไปสู้เขาไม่ได้หรอก เพราะเขียนไปมั่วแบบคนไม่มีพื้นฐาน image processing อะไรเลย แต่ก็สนุกดีได้ลองใช้ PIL เป็นครั้งแรกด้วย

May 14, 2015

Sexy Love



ช่วงนี้ทำงานแรงงานครับ ไม่ค่อยได้ใช้สมองเท่าไหร่ เลยต้องเปิดเพลงวนไปเรื่อยๆ ให้มันรู้สึกสดชื่นตลอดเวลา

แล้ว YouTube ก็วนเพลง Sexy Love มาให้

รู้ตัวอีกที 2 ชั่วโมงถัดมา ก็ยังกดเล่นซ้ำ ฟังเพลงนี้ซ้ำๆ อยู่ครับ

ท่าเต้นโรบอทนี่มันแจ่มจริงๆ ให้ดิ้นตายสิ :3

May 7, 2015

Entertainment



เพลงนี้เป็นเพลงประกอบเครดิตหนัง Now You See Me (2013) ครับ คือทั้งเรื่องไม่ได้ใช้เพลงนี้มาประกอบเลยนั่นแหละ ถ้าไม่นั่งดูเครดิตหนังก็ไม่รู้ว่ามี

ฟังแล้วสนุกดี ได้กลิ่นหนังจีนสมัยก่อนลอยมาแต่ไกลเลย (ทั้งๆ ที่เป็นวงจากฝรั่งเศส) สงสัยเพราะว่าเมโลดี้อยู่ในกุญแจไดอาโทนิคด้วย แต่มันก็ไม่ได้ออกบลูแจ๊สแม้แต่น้อย กลับเป็นร๊อคมันส์ๆ ให้โยกหัวตามได้

ก็ถือว่าเป็นการผสมผสานที่น่าสนใจดี นั่งฟังได้ทั้งวัน