Combination

เขียนฟังก์ชั่น comb(n,m) ให้สร้าง combination ของเลข 1 ถึง n จำนวน m ตัว เช่น

>>> print comb(5,3)
[[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]]

โค้ดไม่สวยนะครับ แบบว่าทำไปแก้ไป

>
def comb(n,m)
  stack = Array.new(m)
  count = 0
  (1..n).each do |a|
    (a..n).each do |i|
      stack.clear
      stack.push(a)
      count = 1
      ((i+1)..(n+1)).each do |j|
        if count == m then
          count -= 1
          puts stack.to_s
          stack.pop
        end
          stack.push(j)
          count+=1
      end
    end
  end
end
 
comb(5,3)
</blockcode>
sugree's picture

กำลังอยากได้ไอเดียแบบ non-recursive พอดี

แต่ดูแล้วเหมือนทำ recursive ด้วยตัวเองนะเนี่ยะ (แบบใช้ stack เก็บ state)

ตอนแรกผมก็จะคิดแบบ recursive นั่นแหละครับแต่ว่าคิดไม่ออกว่าจะเขียน method ยังไง
แต่ว่าไล่ stack ได้นะครับ ก็เลยใช้ stack เลย

sugree's picture

อย่างน้อยก็ไม่จำกัดที่ stack ของภาษา ว่าแต่มีแบบ random access มั๊ยครับ

ของผมขอส่งช้าหน่อยนะครับ
ช่วงนี้นอนน้อย สมองส่วน programming ไม่ทำงานแล้ว

อันแรกที่ผมทำผมว่ายังผิดอยู่ก็เลยกลับไปแก้และลองพยายามไม่ใช้stack ก็ได้ตามนี้ครับ

>
 
def comb(n,m)
    result = []
    start = Array.new(m)
    max = Array.new(m)
    0.upto(m-1) do |i| 
        start[i] = i+1
        max[i] = (n-m+i+1)
    end
    result << "["+ start.join(",")+"]"
    while start[0] < max[0]
        last_change = m-1
        start[m-1]+=1
        (m-1).downto(1) do |i|
            if start[i] > max[i] then
                start[i-1]+=1
                last_change = i-1
            else 
                break
            end
        end
        (last_change).upto(m-2) do |i|
            start[i+1] = start[i]+1
        end
        result << "["+ start.join(",")+"]"
    end
"["+result.join(",")+"]"
end
print "Enter n: "
n = gets.to_i
print "Enter m: "
m = gets.to_i
print comb(n,m)+"\n"
</blockcode>

in haskell

comb xs cnt
   | cnt == 0  = [[]]
   | otherwise = [x:ys | x <- xs, ys <- comb (dropWhile (<= x) xs) (cnt - 1)]

Note:
สำหรับคนไม่คุ้นกับ List comprehension
อ่านดูได้ที่นี่ List Comprehension

อยากคิดเป็นฟังก์ชันแบบนี้ได้บ้างนะครับ แต่คิดไงก็คิดไม่ออก

ใช่คิดยาก
ขนาดผมฝึกมาเรื่อยๆแล้ว ก็ยังรู้สึกยากอยู่

สำหรับผม ที่ช่วยได้เยอะ ก็คือต้องอาศัยดู code คนอื่นบ่อยๆ

sugree's picture

ผมชอบ (dropWhile (<= x) xs) จัง

in ruby

สร้าง helper ใน class Integer

class Integer
  def cartesian(xs)
    xs.map do |n|
      [self, n].flatten
    end
  end
end

ทำให้เราใช้คำสั่งแบบนี้ได้

irb(main):023:0> 2.cartesian [3,4,5]
[[2, 3], [2, 4], [2, 5]]

จากนั้นก็เขียน comb function

def comb(xs,m)
  return xs if m == 1
  xs.inject([]) do |acc, x|
    acc + x.cartesian(comb(xs.select{|n| n > x}, m-1))
  end  
end
 
irb(main):066:0> comb [1,2,3,4,5], 3
[[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]]

Note: จะเห็นได้ว่า ก็ลอกแนวมาจาก haskell version นั่นแหล่ะ

ย้าย Codenone

ประกาศย้าย Codenone ไปใช้ Forum ของ Blognone แทนครับ ตามไปตั้งกระทู้ต่อได้ที่ Codenone Forum (รายละเอียดอ่านจากกระทู้ ย้าย Codenone ไปรวมกับ Blognone)

กระทู้เก่าๆ จะย้ายตามไปในภายหลัง ตอนนี้ปิดการโพสต์กระทู้ไว้ เหลือไว้เฉพาะอ้างอิงเท่านั้น