YOLO เป็นสแลงของวัยรุ่นในแถบประเทศที่พูดภาษาอังกฤษ มันย่อมาจากประโยคที่บอกว่า you only live once ที่แปลว่า "คุณมีชีวิตอยู่แค่ครั้งเดียว" ดังนั้นจะทำอะไรก็ทำไปเถอะ น่าเสียดายว่าหลายคนเอาไปใช้ในทางที่สุดโต่งอย่างการทำผิดกฎหมาย ทำให้คำนี้โดนมองในแง่ลบอย่างหนัก ต่างไปจากวลี carpe diem ในภาษาละตินที่แปลว่า "จงฉกฉวยชั่นขณะนี้ไว้" มันเป็นคำประพันธ์โบราณโดย Horace เมื่อกว่าสองพันปีก่อน และได้รับการเผยแพร่ซ้ำผ่านหนังขึ้นหิ้งอย่าง Dead Poet Society ทำให้วลีนี้ดูสุขุมนุ่มลึกกว่าคำข้างต้นที่เป็นได้เพียงสบถของพวกนอกคอก
แต่บทความนี้ไม่ได้มาเจาะลึกเรื่องทางประวัติศาสตร์ สังคม หรือแม้กระทั่งความหมายของคำนั้นหรอกครับ
เพราะสิ่งที่น่าสนใจยิ่งกว่า คือแนวคิดที่แฝงไว้ว่า "ชีวิตนี้มีแค่ครั้งเดียว"
หลายคนอาจจะรู้สึกว่าแนวคิดเช่นนี้เป็นสิ่งแปลกประหลาด ซึ่งก็เข้าใจได้ เพราะแทบทุกศาสนาบนโลก ต่างพูดเรื่องนี้ในทางตรงกันข้ามว่า "ยังมีเรื่องอื่นๆ อีกหลักความตาย" เช่น แนวคิดเรื่องสวรรค์และนรก แนวคิดการเวียนว่ายตายเกิด
จุดที่ทำให้ศาสนาต่างๆ สร้างความเชื่อเช่นนั้นได้แม้ร่างกายจะสูญสิ้นไป ก็ด้วยแนวคิดที่ว่าร่างกายนั้นประกอบขึ้นมาจากสิ่งของในโลกนี้อย่างกระดูกเลือดเนื้อ และสิ่งของในโลกอื่นอย่างวิญญาณ (soul) ซึ่งหากมันรวมอยู่กับร่างกายแล้วจะช่วยให้มีชีวิต มีความคิด มีความรู้สึก แต่หากแยกออกจากกันก็จะทำให้ร่างกายนั้นตายไป
พูดง่ายๆ ว่าวิญญาณนั้นอยู่ในมิติอื่นที่เราไม่สามารถสังเกตได้นั่นเอง
และแน่นอนว่า เมื่อไหร่ก็ตามที่วิทยาศาสตร์จะพยายามสังเกตวิญญาณให้ได้ ศาสนาก็จะหลบเลี่ยงไปอธิบายวิญญาณในรูปแบบอื่นแทน ดังนั้นบทความนี้จะยังไม่มาเล่นวิ่งไล่จับกัน แต่จะฟันธงไปก่อนเลยว่าวิญญาณนั้นไม่มี เพื่อที่จะได้สำรวจความคิดที่งอกเงยขึ้นมาอย่างเต็มที่
เมื่อเราปฏิเสธเรื่องวิญญาณไปแล้ว จะเห็นได้ว่าส่วนที่เหลือก็ง่ายทันที ความรู้สึกนึกคิด ความฝัน ความตระหนักในตัวตน (self-awareness) ต่างเป็นกิจกรรมที่เกิดขึ้นในสมองทั้งนั้น เมื่อสมองหยุดทำงานลง ก็หมายถึงความตายที่เราคุ้นเคยกันดี
แต่ความตายนั้นเป็นจุดจบจริงหรือ?
แบบจำลองจักรวาลที่ได้รับการยอมรับอย่างมากที่สุด ณ ขณะนี้ เสนอว่าจักรวาลนั้นมีจุดกำเนิด (big bang) เมื่อสิบสี่พันล้านปีที่แล้ว และขยายตัวมาเรื่อยๆ ด้วยอัตราเร่งที่สูงอย่างน่าใจหายถึงทุกวันนี้
หากจักรวาลยังคงขยายตัวเช่นนี้ต่อไป แรงโน้มถ่วงจะเป็นฝ่ายพ่ายแพ้ ท้ายที่สุดแล้วจักรวาลจะเย็นตัวลงเพราะไม่เหลือแหล่งพลังงานอีกต่อไป ดาวทั้งหลายดับสิ้นไปจนมีแต่ความมืดมิดปกคลุมทุกหนแห่ง ช่วงเวลาดังกล่าวจะยาวนานตราบชั่วนิจนิรันดร์และไม่มีวันย้อนกลับ
เมื่อนั้นแหละ ที่ความตายได้กลายเป็นจุดจบอย่างแท้จริง สำหรับชีวิตที่มีโอกาสเพียงแค่หนึ่งครั้ง
Aug 13, 2015
Aug 6, 2015
ตรวจ 2^n
วันก่อนเห็นทวีตนี้ของ @Methuz
แล้วก็มานึกได้ว่าเคยเห็นโจทย์นี้มานานแล้วนะ พอนั่งคิดต่ออีกหน่อยก็พบว่าปรับปรุงให้ดีขึ้นได้อีก โดยเปลี่ยนส่วนที่กลับข้างบิตแล้วเปรียบเทียบกับตัวเอง ไปเป็นไม่ต้องกลับข้างบิตแต่เอาไปเทียบกับศูนย์แทน กล่าวคือ
ถ้าไม่เชี่ยววิเคราะห์อัลกอริทึม ดูแค่นี้อาจยังมองไม่เห็นว่าปรับปรุงดีขึ้นจริงหรือเปล่า เลย compile เป็น assembly ออกมาแล้วไล่ดูทีละบรรทัดเลย
คำสั่งที่ใช้ compile ให้เป็น assembly (GAS syntax) คือ
อันนี้คือผลลัพธ์แบบแรกที่คุณ @Methuz เสนอ (ตัดมาเฉพาะส่วนฟังก์ชันที่สำคัญนะครับ)
และอันนี้เป็นแบบที่ปรับปรุงแล้ว
โอเค ใครอ่านถึงตรงนี้แล้วไม่งงก็กดปิดได้ เพราะเห็นชัดว่าปรับปรุงให้ดีขึ้นจริง
ส่วนใครงงอยู่ เดี๋ยวมาไล่โค้ดกันทีละบรรทัดกันครับ :D
แต่ก่อนอื่น หากอยากอ่าน assembly รู้เรื่อง ก็ต้องไปทำความคุ้นเคยกับ register เสียก่อน (พวกที่เขียนด้วยเครื่องหมาย
ในฟังก์ชันที่ตัดมานี้มี register ที่สำคัญอยู่ 2 ตัว คือ
ดังนั้นหากค่าของ
ส่วนหน้าที่พิเศษของ register ทั้ง 2 ตัวในที่นี้คือ
พอคุ้นเคยกับ register เหล่านี้แล้ว คราวนี้ก็ได้เวลามาดูโค้ดจริงๆ เสียที
บรรทัดแรกนี้สั่งล้างค่า
สองบรรทัดต่อมาเป็นการเช็คว่า
บรรทัดต่อๆ มาเป็นชุดคำสั่งในส่วนหลังที่ถามว่า หากมอง
ชุดบรรทัดพวกนี้มีหน้าที่และผลลัพธ์เหมือนกับชุดบรรทัดในหัวข้อย่อยก่อนเลยครับ สิ่งที่แตกต่างคือวิธีการเท่านั้น โดยโค้ดเริ่มทำงานจากคัดลอกค่า
ถึงตอนนี้การคำนวณหลักๆ ได้เสร็จสิ้นหมดแล้ว ที่ยังไม่เสร็จคือเตรียมค่าที่จะส่งคืน เพราะก่อนหน้านี้เราใช้
สุดท้ายก็ได้เวลาส่งคืนคำตอบครับ สังเกตว่า compiler เพิ่มความมั่นใจให้คำสั่งนี้ด้วยการเติม
สุดท้ายการวิเคราะห์อัลกอริทึม/โค้ดยังไม่พอ นี่คือผลเชิงประจักษ์ของเวลาที่ใช้ไปสำหรับการตรวจว่าเลขดังกล่าวเป็น 2^n หรือไม่ สำหรับจำนวนเต็มบวกทุกตัวที่มีค่าต่ำกว่าสองพันล้านครับ
ก็เร็วต่างกันประมาณ 10%
Best way to check if number is a power of two
return ((x != 0) && ((x & (~x + 1)) == x))
— Midas~♫ (@Methuz) June 24, 2015แล้วก็มานึกได้ว่าเคยเห็นโจทย์นี้มานานแล้วนะ พอนั่งคิดต่ออีกหน่อยก็พบว่าปรับปรุงให้ดีขึ้นได้อีก โดยเปลี่ยนส่วนที่กลับข้างบิตแล้วเปรียบเทียบกับตัวเอง ไปเป็นไม่ต้องกลับข้างบิตแต่เอาไปเทียบกับศูนย์แทน กล่าวคือ
@Methuz ((x != 0) && ((x & (x - 1)) == 0))
— เนยสดร้องไห้ทำมัย (@neizod) June 24, 2015ถ้าไม่เชี่ยววิเคราะห์อัลกอริทึม ดูแค่นี้อาจยังมองไม่เห็นว่าปรับปรุงดีขึ้นจริงหรือเปล่า เลย 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 มาวางบนกล่องข้อความหน้าเว็บก็พอ
ส่วนกล่องข้อความก็เปลี่ยนจาก
อยากลองเล่น? ขั้นแรกให้พิมพ์ตามนี้ลงไปใน address bar ของเว็บเบราเซอร์เพื่อสร้าง WYSIWYG ก่อนครับ
ขั้นต่อมาก็ไปเปิดเอกสาร Word ซักอัน (หรือจะสร้างใหม่ก็ได้) แล้วลองคัดลอกข้อความมาวางบนหน้าเว็บนั้นดู
ส่วนอะไรที่เวิร์กไม่เวิร์กบ้าง เท่าที่ลองคร่าวๆ ได้ผลตามตารางนี้ครับ
(จากที่สังเกต ผลลัพธ์ตอนนำข้อความมาวางเหมือนจะไม่มีความต่างกันในแต่ละเว็บบราวเซอร์ ดังนั้นจึงสนใจเฉพาะตอนคัดลอกมาจากโปรแกรมแก้ไขเอกสารอย่างเดียวครับ)
ถ้าใช้เว็บสำเร็จรูปทั่วไปก็อาจจะไม่ยากเท่าไหร่ เช่น 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 หายไปแล้ว เลยมาจดไว้ก่อน เผื่อเจอเหตุการณ์ประมาณนี้อีกจะได้ลดเวลาลองผิดลองถูก
เรื่องประหลาดคือ ในสคริปต์ถ้าสลับตำแหน่งเป็นกด Shift ค้างไว้ก่อนแล้วค่อยกด Alt มันก็เปลี่ยนภาษาได้นะ แต่พอเปลี่ยนเสร็จเคอร์เซอร์จะหลุดโฟกัสไปเลย ไม่รู้ว่าเกิดอะไรขึ้น :\
- เช็คให้แน่ใจว่าการเปลี่ยนภาษาบน Windows ใช้ปุ่ม
Alt+Shiftเสียก่อน - ดาวน์โหลดและติดตั้งโปรแกรม AutoHotKey
- กด
Win+Rพิมพ์shell:startupมันจะเปิดแฟ้มเก็บโปรแกรมที่ถูกเรียกอัตโนมัติตอนเปิดเครื่องมาให้ - คลิกขวาเลือกสร้างสคริปต์ AutoHotKey (อย่าสร้างไฟล์เปล่าๆ เพราะจะไม่ได้ค่าปริยายมาด้วย) แล้วเพิ่มคำสั่งนี้ต่อท้ายไฟล์
Capslock:: Send {Alt Down}{Shift}{Alt Up}
เรื่องประหลาดคือ ในสคริปต์ถ้าสลับตำแหน่งเป็นกด Shift ค้างไว้ก่อนแล้วค่อยกด Alt มันก็เปลี่ยนภาษาได้นะ แต่พอเปลี่ยนเสร็จเคอร์เซอร์จะหลุดโฟกัสไปเลย ไม่รู้ว่าเกิดอะไรขึ้น :\
May 25, 2015
ลองใช้ Lamy ครั้งแรก
ชีวิตนี้ไม่เคยคิดว่าจะใช้ปากกาด้ามแพงเกินร้อยบาทครับ แต่ก็ได้ Lamy มาฟรีด้ามนึงเป็นของขวัญวันรับปริญญา
ดองไว้เป็นปีแล้วเพราะไม่รู้ว่าใช้ยังไง จนกระทั่งไม่กี่วันก่อนที่ปากกาทุกด้ามจงใจหมึกตัน เลยได้ฤกษ์เอาออกมาลองใช้ซะเลย
ด้ามที่ได้มานี้หัวเป็นขนาด M ครับ โดยรวมแล้วเอาไปเขียนลื่นสนุกดี แม้มีสะดุดบางจังหวะ
แต่ตอนเอามาวาดรูปนี่รู้สึกควบคุมน้ำหนักเส้นไม่ได้เลย (ไม่เหมือนหัวลูกลื่นที่สามารถฝนบางๆ น้อยกว่าขนาดที่บอกไว้ได้) ...
สรุปแล้วไม่ค่อยชอบเท่าไหร่แฮะ นี่ผมแปลกไปเองหรือเปล่าเนี่ย?
ดองไว้เป็นปีแล้วเพราะไม่รู้ว่าใช้ยังไง จนกระทั่งไม่กี่วันก่อนที่ปากกาทุกด้ามจงใจหมึกตัน เลยได้ฤกษ์เอาออกมาลองใช้ซะเลย
ด้ามที่ได้มานี้หัวเป็นขนาด M ครับ โดยรวมแล้วเอาไปเขียนลื่นสนุกดี แม้มีสะดุดบางจังหวะ
แต่ตอนเอามาวาดรูปนี่รู้สึกควบคุมน้ำหนักเส้นไม่ได้เลย (ไม่เหมือนหัวลูกลื่นที่สามารถฝนบางๆ น้อยกว่าขนาดที่บอกไว้ได้) ...
สรุปแล้วไม่ค่อยชอบเท่าไหร่แฮะ นี่ผมแปลกไปเองหรือเปล่าเนี่ย?
May 22, 2015
Voronoi จากภาพ
วันก่อนไปเจอคำถามใน Code Golf มา เค้าให้สร้าง Voronoi เพื่อเลียนแบบภาพต้นฉบับครับ
อ่านแล้วสนุกดี ผลงานของคนอื่นที่ได้ๆ ก็ดูเหมือนงานศิลป์พวก Divisionism/Pointillism ด้วย เลยลองกลับมาเขียนเองแบบมั่วๆ บ้าง 555
เอาไปสู้เขาไม่ได้หรอก เพราะเขียนไปมั่วแบบคนไม่มีพื้นฐาน image processing อะไรเลย แต่ก็สนุกดีได้ลองใช้ PIL เป็นครั้งแรกด้วย
อ่านแล้วสนุกดี ผลงานของคนอื่นที่ได้ๆ ก็ดูเหมือนงานศิลป์พวก 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) ครับ คือทั้งเรื่องไม่ได้ใช้เพลงนี้มาประกอบเลยนั่นแหละ ถ้าไม่นั่งดูเครดิตหนังก็ไม่รู้ว่ามี
ฟังแล้วสนุกดี ได้กลิ่นหนังจีนสมัยก่อนลอยมาแต่ไกลเลย (ทั้งๆ ที่เป็นวงจากฝรั่งเศส) สงสัยเพราะว่าเมโลดี้อยู่ในกุญแจไดอาโทนิคด้วย แต่มันก็ไม่ได้ออกบลูแจ๊สแม้แต่น้อย กลับเป็นร๊อคมันส์ๆ ให้โยกหัวตามได้
ก็ถือว่าเป็นการผสมผสานที่น่าสนใจดี นั่งฟังได้ทั้งวัน
May 6, 2015
Noah
ไม่กี่วันก่อนช่อง HBO เอาเรื่องนี้มาฉายครับ ดูจบแล้วคำพูดนี้พุ่งเข้ามาในหัวเลย
God is dead
-- Friedrich Nietzsche
เกริ่นก่อนสำหรับใครที่ไม่คุ้นเคยกับศาสนาคริสต์ เรื่องนี้เล่าถึงช่วงแรกที่พระเจ้าสร้างโลกครับ หลังจากสร้างโลกและสรรพสัตว์ทั้งหลายเสร็จ ก็สร้างมนุษย์คู่แรก (อดัม-อีฟ) ขึ้นมาไว้ในสวนเอเดน โดยให้อิสระทุกอย่างยกเว้นอย่างเดียวคือห้ามกินผลไม้ที่อยู่กลางสวน (ตีความกันว่าคือผลแอปเปิล) ช่วงแรกอดัมกับอีฟก็เชื่อฟังพระเจ้าดีอยู่ แต่ก็โดนงูหลอกให้กินผลไม้นั้น พอพระเจ้าจับได้จึงไล่อดัมและอีฟ (และงู) ออกจากสวนเอเดนไป
เวลาผ่านไปหลายชั่วอายุคน พระเจ้าเห็นว่าโลกเสื่อมโทรมลงมาก จึงต้องการชำระล้างโลกด้วยพลังแห่งน้ำ ก่อนลงมือก็ไปกระซิบบอกโนอาห์ (โคตรเหลนของอดัม-อีฟ) ให้เตรียมตัวรับมือก่อน โดยอย่าลืมพาพวกสรรพสัตว์ไปด้วย โนอาห์เลยต่อเรือยักษ์ตามที่พระเจ้าว่า
จุดแตกต่างจากไบเบิลที่เด่นชัดจุดแรกในเวอร์ชันหนัง คือพระเจ้าไม่ได้พูดตรงๆ กับโนอาห์แต่ใช้วิธีฉายภาพภัยพิบัติที่จะเกิดขึ้นให้ดูในฝันแทน (ถ้าเคยดู Prince of Egypt ปี 1998 จะเห็นว่าพระเจ้าอวตารร่างลงมาเป็นลูกไฟเพื่อพูดคุยกับโมเสสตรงๆ เลย) อันที่จริง หนังเรื่องนี้ทั้งเรื่องไม่ได้แสดงตัวตนหรือคำพูดของพระเจ้าเลย
จุดต่อมาคือมนุษย์ที่โนอาห์พาขึ้นเรือไปนั้น ไม่สามารถมีลูกหลานต่อไปได้ ซึ่งก็กลายเป็นดราม่าหลักของเรื่องเมื่ออิล่า แฟนสาวกับลูกคนหนึ่งโนอาห์ (นำแสดงโดยเฮอร์ไมโอนี่ :P) ที่เป็นหมันมาตั้งแต่เด็ก ดันตั้งท้องบนเรือซะงั้น โนอาห์ซึ่งคิดว่าความเสื่อมใดๆ ในโลกล้วนเกิดจากน้ำมือของมนุษย์ จึงต้องการฆ่าลูกของอิล่าเสียเมื่อเธอคลอด
การสร้างหนังจากเรื่องราวที่รายละเอียดต้นทางน้อยแบบนี้ (เรื่องของโนอาห์มีไม่ถึงครึ่งของครึ่งในหนังสือปฐมกาล ถ้าเทียบกับโมเสสที่ได้หนังสืออพยพไปทั้งเล่ม) มันต้องมีการตีความแต่งเติมปรับแต่งเนื้อเรื่องไปมาก ตรงนี้เข้าใจและยอมรับได้นะ และหนังที่สร้างออกมาจากการตีความนี้ก็สนุกพอตัวเลยหละ
แต่ดูแล้วก็อดไม่ได้ที่จะคิดไปว่า พระเจ้าในหนังเรื่องนี้ ไม่ได้มีพลังอำนาจอะไรเลย
การที่พระเจ้าไม่เคยปรากฎตัวขึ้นมาแม้แต่ครั้งเดียว เราอาจตีความได้ว่าโนอาห์ดันฝันเห็นแฟนตาซีที่น่ากลัวมากๆ แล้วหมกมุ่นกับมันจนต้องสร้างเรือขึ้นมา ไม่จำเป็นต้องมีพระเจ้ามาเกี่ยวข้องเลย (ใครไม่เคยฝันแฟนตาซีน่าตื่นเต้นแบบนี้บ้าง?) พอฝนตกหนักน้ำท่วมก็โป๊ะเชะกับที่โนอาห์กลัวพอดี
ยิ่งพอถึงฉากสุดท้ายที่อิล่าบอกโนอาห์ หลังจากโนอาห์ตัดสินใจไม่ฆ่าล้างเผ่าพันธุ์มนุษย์ ปล่อยให้ลูกสาวของอิล่ามีชีวิตต่อไปหลังคลอด และอวยพรให้มีลูกเต็มบ้านมีหลานเต็มเมือง
The choice was put in your hands because he put it there.
-- Ila, Noah (2014)
ยิ่งแสดงให้เห็นว่ามนุษย์ไม่จำเป็นต้องพึ่งพาพระเจ้าก็ได้ หรือพูดอีกนัยหนึ่งว่ามนุษย์ไม่จำเป็นต้องพิสูจน์ทราบให้ได้ว่าพระเจ้ามีตัวตนจริงหรือไม่ เนื่องจากผลลัพธ์ที่ได้จากการพิสูจน์นี้ ไม่ได้ก่อให้เกิดความแตกต่างต่อสิ่งที่จะเกิดตามมาเลย (ทำนองเดียวกับที่ไอนสไตน์บอกว่าอีเทอร์ไม่จำเป็นนั่นแหละ)
พระเจ้าที่ไร้อำนาจเช่นนี้ ก็คงไม่ต่างจากพระเจ้าที่ตายแล้วอย่างที่นิทเช่บอกไว้
Apr 16, 2015
Code Jam 2015 รอบคัดเลือก
ปีนี้ประมาทไปเยอะครับ คือตอนแรกตั้งใจว่าจะถ่างตามาเริ่มเล่นตั้งแต่ปล่อยโจทย์ตอน 6 โมงเช้าเลย แต่สุดท้ายก็เผลอหลับไปตอนตี 4 ตื่นมารู้ตัวอีกทีก็บ่ายแล้ว เลยพักสมองออกเล่นบอร์ดเกมกะ @tamwb และ @aibig ตามนัดปาร์ตี้สงกรานต์ แถมกลับมาก็ยังไม่มีสมาธิแก้โจทย์มัวแต่ดู MV เกาหลีอีก กว่าได้เริ่มทำแข่งจริงจังก็เหลือเวลาไม่กี่ชั่วโมงแล้ว
ตอนอ่านโจทย์ผ่านๆ ทุกข้อนี่คิดว่าง่าย ปีนี้ได้แก้มือเก็บคะแนนเต็มแน่ๆ ... ที่ไหนได้ bug ซ่อนเร้น 1 ข้อ แล้วก็ตีความโจทย์อีก 1 ข้อ สุดท้ายแก้ไม่ทัน ได้ส่งแค่ 2 ข้อ ดีแค่ไหนแล้วที่ยังไม่ตกรอบ 555
ข้อแรกง่ายฮะ แต่ถ้ายังอ่านโจทย์แล้วไม่อินขอให้ดูคลิปนี้ตอนนาทีที่ 7:05 และ 7:23 (เรียบร้อยแล้วก็กลับไปดูตั้งแต่ต้นด้วยจะดีมาก) :P
ส่วน code ก็ประมาณ
import Text.Printf (printf)
import Data.List.Split (splitOn)
invites audience =
let aux [] _ _ inv = inv
aux (p:ps) shy acc inv = aux ps (shy+1) (acc+p+more) (inv+more)
where more = max 0 (shy-acc)
in aux audience 0 0 0
test t = do
it <- getLine
let [_, ps] = splitOn " " it
printf "Case #%d: %d\n" t $ invites [read [p] :: Integer | p <- ps]
main = do
loop <- getLine
sequence_ [test t | t <- [1..read loop :: Integer]]
(อย่างไรก็ตาม เท่าที่เคยไปคอนเสิร์ตหลายครั้ง รู้สึกว่าคนไทยจะมีค่าความอายเป็น infinity นะ)ข้อสองข้ามไปนะฮะ ทำไม่ได้ (ข้อสี่ด้วย)
ส่วนข้อสามนี่สนุกดี ถ้ายังจำเนื้อหาม.ปลายได้ เราจะมีเลขมหัศจรรย์ตัวนึงเรียกว่า
i ซึ่งมีนิยามที่สวยงามเรียบง่าย คือi^2 = -1สังเกตเพิ่มอีกหน่อยก็จะเห็นว่า
i^4 = 1 (multiplicative identity)โจทย์เพิ่มความสนุกโดยให้ตัวเลขมหัศจรรย์
j และ k มาด้วย ทั้งสองตัวที่เพิ่มเข้ามานี้ มีนิยามแบบเดียวกันกะ i เด๊ะๆ เลย เพียงแต่มันไม่ใช่ i เพราะเรานิยามการคูณระหว่างตัวเลขเหล่านี้ว่าij = k jk = i ki = jความซับซ้อนของตัวเลขชุดนี้ยังเพิ่มขึ้นไปอีก เมื่อเรานิยามให้มันไม่มีสมบัติสลับที่ด้วย ซึ่งในที่นี้คือ
ji = -k kj = -i ik = -jเพื่อความสะดวก สมมติชื่อให้เลขชุดนี้ว่า
ℍu ละกันครับ โชคดีที่อย่างน้อยในเซ็ต ℍu = {1, i, j, k} มันยังมี- สมบัติปิดบนการคูณ คือ ถ้าใช้การคูณอย่างเดียวบนสมาชิกของเซ็ต
ℍuผลลัพธ์ที่ได้ก็จะเป็นสมาชิกของเซ็ตℍuด้วย - สมบัติจัดกลุ่มการคูณ คือ
a(bc) = (ab)cสำหรับทุกa, b, c ∈ ℍu
จาก
X, LS ที่โจทย์ให้ การแก้ปัญหาแบบตรงๆ ก็คือวิ่งผ่านชุดตัวเลข LS เป็นจำนวน X ครั้ง โดยเลขแต่ละตัวเลข s ∈ LS ที่วิ่งผ่าน ก็จัดการคูณเก็บไว้ในใจไปเรื่อยๆ ถ้าเจอตัวเลขครบทั้ง i, j, k และหางที่เหลือคูณกันออกมาเป็น 1 ก็ตอบว่าถูกได้เลยถ้าอ่านมาจนถึงตรงนี้แล้วยังงงไม่หาย ลองดูการทำงานตามขั้นตอนวิธีด้วยตัวอย่างนี้
X = 10 LS = ji sequence: ji ji ji ji ji ji ji ji ji ji running: --->--->-------->-----------> found: i j k 1โจทย์ข้อนี้ฉลาดตรงที่เอาตัวเลข
Xขนาดใหญ่มากๆ มาขู่ ทั้งที่เราสามารถ modulo มันทิ้งให้เหลือแค่หลักสิบได้ (ซึ่งก็คือ คำถามชุดใหญ่จะมีความยากแทบไม่ต่างกับคำถามชุดเล็กเลย)การลดขนาด
X ทำได้โดยใช้ข้อสังเกตที่ว่า i^4 = j^4 = k^4 = 1 ซึ่งบอกเป็นนัยอยู่ 2 อย่างคือ- ถ้าวิ่งคูณหา
i, j, kแต่ละตัวเกิน 4 รอบแล้วยังไม่เจอ ไม่ต้องวิ่งต่อ เพราะแต่ละรอบที่วิ่งเพิ่ม จะไม่ได้ผลลัพธ์ใหม่แล้ว - หลังจากหา
i, j, kได้ครบแล้ว หางที่เหลือสามารถ mod 4 ได้
i, j, k) โดยแต่ละครั้งวิ่งไม่เกิน 4 รอบ ซึ่งก็เท่ากับวิ่ง 12 รอบ แล้วรอบที่เหลือก็จับ mod 4 ได้เลยหรือพูดจาภาษา code ก็คือ
def correct_misspelling(ijks, x):
x = min(x, 12+(x%4))
accumulate = Quaternion('+1')
searching = [Quaternion(it) for it in 'kji']
for _ in range(x):
for it in ijks:
accumulate *= Quaternion(it)
if searching and searching[-1] == accumulate:
accumulate = Quaternion('+1')
searching.pop()
return not searching and accumulate == Quaternion('+1')
for case in range(int(input())):
_, x = [int(n) for n in input().split()]
ijks = input().strip()
ans = 'YES' if correct_misspelling(ijks, x) else 'NO'
print('Case #{}: {}'.format(case+1, ans))
ข้อนี้ถ้าจะ optimize เพิ่มก็ยังทำได้อีก (code จริงที่เอาไปส่งก็เขียนสวยน้อยกว่านี้เพราะมัวแต่ optimize มากเกินไป) โดยเรามีข้อสังเกตดังนี้aba = bสำหรับทุกa, b ∈ ℍu- เราสามารถทดเก็บค่าสุดท้ายที่ได้จากการวิ่งผ่านเพื่อหาผลคูณของทุกตัวใน
LSไว้ได้ แล้วพอจะวิ่งผ่านLSอีกรอบก็ใช้ค่าที่ทดไว้ได้เลย - ในการทดลองจริง จำนวน
Xที่จำเป็นต่อการคำนวณไม่เคยมีค่าเกิน 8 เลย ซึ่งก็คือx = min(x, 4+(x%4))
Subscribe to:
Posts (Atom)






