Just algorithm!
C#; การสร้าง set ที่มีจำนวนสมาชิกที่เป็นอนันต์

โดยทั่วไปใน .NET มี Type จำพวก Array, List, และ Collection ที่สามารถแตกย่อยสมาชิกที่อยู่ใน set ได้
และ Type เหล่านี้ มี Method ต่าง ๆ สำหรับเรียกข้อมูลสมาชิกขึ้นมา
เช่น ถ้าเกิดคุณอยากจะทราบว่ามี object อยู่ใน list หรือไม่ คุณสามารถเขียนดังนี้:

bool exists = list.Contains(obj);

หรือถ้าเกิดคุณอยากจะดึงสมาชิกใน list 10 อันแรก คุณสามารถเขียนดังนี้:

var newList = list.Take(10);

แต่ทุก Type ที่เป็น set จะมีจำนวนสมาชิกจำกัด แต่สำหรับ set ที่มีสมาชิกไม่จำกัดล่ะ? มีอะไรบ้าง?
เท่าที่นึกได้ก็เช่น จำนวนเฉพาะ ลำดับ Fibonacci ลำดับตัวเลขต่าง ๆ
ซึ่งจะเป็นไปได้ไหมถ้าเกิดเราอยากจะเช็คว่า 34567 เป็นจำนวนเฉพาะหรือเปล่า โดยเขียนว่า:

var isPrime = prime.Contains(34567);

หรืออยากจะได้ลำดับ Fibonacci 1000 ตัวแรก โดยเขียนว่า:

var milFib = fib.Take(1000);

ปัญหาคือ เราจะเขียน Class สำหรับแทน set ที่มีสมาชิกไม่จำกัดอย่างไร?
เรามาเริ่มสร้างกันโดยเริ่มจาก based class ซึ่งจะเป็น Class หลักสำหรับแทนลำดับที่มีสมาชิกไม่จำกัด

public abstract class BaseSequence<T> {
    protected abstract IEnumerable<T> Enumerate();

    public T ElementAt(int index) {
        return Enumerate().ElementAt(index);
    }
    public T First() {
        return Enumerate().First();
    }
    public T First(Func<T, bool> predicate) {
        return Enumerate().First(predicate);
    }
    public IEnumerable<T> Take(int count) {
        return Enumerate().Take(count);
    }
    public IEnumerable<T> TakeWhile(Func<T, bool> predicate) {
        return Enumerate().TakeWhile(predicate);
    }
    public IEnumerable<T> TakeWhile(Func<T, int, bool> predicate) {
        return Enumerate().TakeWhile(predicate);
    }
}


จะเห็นได้ว่า based class นี้มี Method ในการดึงสมาชิกคล้าย ๆ กับ set ทั่วไป
โดยที่ First จะเป็นคำสั่งเพื่อดึงสมาชิกตัวแรก
ElementAt จะเป็นคำสั่งเพื่อดึงสมาชิกตัวที่ระบุ
Take เป็นคำสั่งสำหรับดึงสมาชิกเป็นจำนวนที่กำหนด
ส่วน TakeWhile เป็นคำสั่งสำหรับดึงสมาชิกตามเกณฑ์ที่กำหนด
และ Class ที่ inherite มาจาก based class จะต้อง implement method ชื่อว่า Enumerate
ซึ่งจะเป็น method สำหรับ generate สมาชิกในลำดับต่อไป

คราวนี้เรามาสร้าง Class สำหรับลำดับง่าย ๆ กัน Class นี้เราจะแทนลำดับตัวเลขทั่ว ๆ ไป
ยกตัวอย่าง เช่น ลำดับเลขคี่ 1,3,5,7,... ลำดับเลขกำลังสอง 1,4,9,16,...

public class GenericSequence<T> : BaseSequence<T> {
    Func<T, T> _func;
    T _seed;

    public GenericSequence(Func<T, T> func) {
        _seed = default(T);
        _func = func;
    }
    public GenericSequence(T seed, Func<T, T> func) {
        _seed = seed;
        _func = func;
    }

    protected override IEnumerable<T> Enumerate() {
        T value = _seed;

        yield return value;
        while (true) {
            value = _func(value);
            yield return value;
        }
    }
}


โดยที่ seed หมายถึง สมาชิกตัวแรกของลำดับ
และ func หมายถึง function สำหรับสร้างลำดับถัดไป โดยมี input เป็นสมาชิกตัวก่อนหน้า
เช่น ถ้าคุณอยากจะสร้างลำดับเลขคี่ 1,3,5,7,.... ให้เขียนดังนี้

new GenericSequence<int>(1, x => x + 2)

เรามาแก้โจทย์จากทาง projecteuler.net กันต่อ :)

Find the longest sequence using a starting number under one million.
Defined n = n/2 (n is even)
and n = 3n + 1 (n is odd)
Finish when n is 1

"หาลำดับที่ยาวที่สุดจากตัวเลขใด ๆ ที่น้อยกว่า 1 ล้าน
โดยที่ n = n/2 ถ้า n เป็นเลขคู่
และ n = 3n + 1 ถ้า n เป็นเลขคี่
โดยหยุดเมื่อ n มีค่าเป็นเป็น 1"

//เรามาสร้าง Function กันก่อน
//ถ้า y เป็นเลขคู่ (y = y/2),
//ถ้า y เป็นเลขคี่ (y = 3y + 1)
Func<long, long> sequenceFunc = y =>
    (y % 2 == 0) ? y / 2 : y * 3 + 1;

//Function นี้สำหรับเช็คว่า ตัวเลขที่ Input เข้ามา
//มีจำนวนสมาชิกมากเท่าไหร่
Func<int, int> countSequence = x =>
    new GenericSequence<long>(x, sequenceFunc)
    .TakeWhile(y => y > 1)  //ดึงจำนวนสมาชิกจนกระทั่ง y มีค่าเป็น 1
    .Count();               //แล้วก็นับจำนวนสมาชิก

//แล้วก็แสดงผลลัพธ์
Console.WriteLine(1.To(999999)  //จำนวนใด ๆ ตั้งแต่ 1 - 999,999
    .OrderByDescending(countSequence)   //เรียงลำดับตามจำนวนสมาชิกจากมากไปน้อย
    .First()                    //แล้วดึงตัวเลขแรก ซึ่งจะเป็นตัวเลขที่มีจำนวนลำดับที่มากที่สุด
);


หรือถ้าเก่งแล้ว เขียนอย่างนี้ไปเลยสั้นดี Smiley

Console.WriteLine(1.To(999999).OrderByDescending(x =>
    new GenericSequence<long>(x, y =>
        (y % 2 == 0) ? y / 2 : y * 3 + 1
    ).TakeWhile(y => y > 1).Count()
).First());
//837799


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

* อ่านจบแล้ว กรุณา Comment ด้วยครับ เพื่อเป็น Feedback และเป็นกำลังใจ
* บทความนี้คัดลอกได้ แต่ต้องมี Link หรือ Url กลับมาที่หน้านี้ และเมื่อคัดลอกแล้วกรุณา Comment แจ้งด้วยครับ




Create Date : 05 กรกฎาคม 2551
Last Update : 5 กรกฎาคม 2551 16:32:33 น. 3 comments
Counter : 2090 Pageviews.

 
โอ๋ นับถือ... ขอบคุณนะครับ จะติดตามผลงาน

ผมยังเขียน C# ไม่ค่อยคล่องเลย

คุณ choawman มีหนังสือแนะนำ ไหมอะครับ


โดย: PureSnow IP: 124.120.203.99 วันที่: 6 กรกฎาคม 2551 เวลา:17:12:47 น.  

 
ขออภัยครับ ผมไม่ได้อ่านหนังสือเขียน Program นานละ
ถ้ามีปัญหาเกี่ยวกับ C# หรือ .NET อ่าน Documentation ที่มากับ Visual Studio ดีที่สุด

ถ้าหาไม่เจอก็ถามกูเกิ้ลส่วนใหญ่จะมีคำตอบให้

ถ้าจะให้แนะนำ Resources เกี่ยวกับ C# ดี ๆ

ก็มี //blogs.msdn.com/wesdyer/ คนเขียน blog เป็นคน Develop C# เองเลยครับ

ไม่ก็ //projecteuler.net เนี่ยแหละครับ เวลาตอบโจทย์ได้ จะมีเฉลยให้ดู
โจทย์เดียวกัน แต่แนวคิดในการแก้ปัญหาแต่ละคนมีหลากหลาย
บางคนแก้โจทย์ที่ซับซ้อน ด้วยหลักการง่าย ๆ Code ไม่กี่บรรทัด แถม Run ได้เร็วด้วยครับ


โดย: chaowman วันที่: 6 กรกฎาคม 2551 เวลา:21:18:50 น.  

 
โอ่อันนี้ก็แหล่มๆๆ


โดย: ntbuilder IP: 101.108.24.216 วันที่: 31 ตุลาคม 2554 เวลา:23:04:53 น.  

ชื่อ :
Comment :
  *ใช้ code html ตกแต่งข้อความได้เฉพาะสมาชิก
 

chaowman
Location :
กรุงเทพฯ Thailand

[Profile ทั้งหมด]

ให้ทิปเจ้าของ Blog [?]
ฝากข้อความหลังไมค์
Rss Feed
Smember
ผู้ติดตามบล็อก : 8 คน [?]





New Comments
Group Blog
 
All Blogs
 
Friends' blogs
[Add chaowman's blog to your web]
Links
 

 Pantip.com | PantipMarket.com | Pantown.com | © 2004 BlogGang.com allrights reserved.