Skip to content

Commit df435a5

Browse files
committed
Added Knapsack in Lua
1 parent 9f465e3 commit df435a5

2 files changed

Lines changed: 90 additions & 0 deletions

File tree

Knapsack/Lua/Yonaba/knapsack.lua

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
-- Knapsack problem algorithm implementation
2+
-- See: http://en.wikipedia.org/wiki/Knapsack_problem
3+
4+
-- Pops value at the head of an array
5+
local function pop(s)
6+
local head = s[1]
7+
table.remove(s, 1)
8+
return head
9+
end
10+
11+
-- Note: the items to be passed should be represented this way
12+
-- items = {
13+
-- {name = 'item_name', w = <item_weight>, b = <benefit>},
14+
-- {name = 'item_name', w = <item_weight>, b = <benefit>},
15+
-- ...
16+
-- }
17+
-- In the returned output, each item will receive a new attribute 'p'
18+
-- referring to how much (percentage) of the item has been added into
19+
-- the knapsack.
20+
21+
-- Performs fractional Knapsack. Fractional here means we can
22+
-- select a portion of a item. With that respect, this implementation
23+
-- is greedy.
24+
-- items : an array of items (this array will be sorted regarding
25+
-- their benefits in decreasing order)
26+
-- capacity: the maximum capacity of the knapsack
27+
-- returns : an array of items
28+
local function fractionalKnapsack(items, capacity)
29+
table.sort(items, function(a, b) return a.b > b.b end)
30+
local inKnapsack = {}
31+
while capacity > 0 do
32+
local max_item = pop(items)
33+
max_item.p = max_item.w > capacity and capacity/max_item.w or 1
34+
max_item.b = max_item.p * max_item.b
35+
max_item.w = max_item.p * max_item.w
36+
capacity = capacity - max_item.w
37+
table.insert(inKnapsack, max_item)
38+
end
39+
return inKnapsack
40+
end
41+
42+
return {
43+
fractional = fractionalKnapsack,
44+
}
45+
46+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
-- Tests for knapsack.lua
2+
local knapsack = require 'knapsack'
3+
4+
local total, pass = 0, 0
5+
6+
local function dec(str, len)
7+
return #str < len
8+
and str .. (('.'):rep(len-#str))
9+
or str:sub(1,len)
10+
end
11+
12+
local function run(message, f)
13+
total = total + 1
14+
local ok, err = pcall(f)
15+
if ok then pass = pass + 1 end
16+
local status = ok and 'PASSED' or 'FAILED'
17+
print(('%02d. %68s: %s'):format(total, dec(message,68), status))
18+
end
19+
20+
-- Fuzzy equality test
21+
local function fuzzyEq(a,b) return math.abs(a-b) < 1e-2 end
22+
23+
run('Testing fractional knapsack', function()
24+
local items = {
25+
{name = 'Apple', w = 3, b = 5},
26+
{name = 'Orange', w = 4, b = 5},
27+
{name = 'Salt', w = 2, b = 3},
28+
{name = 'Pepper', w = 1, b = 2},
29+
{name = 'Rice', w = 5, b = 6},
30+
}
31+
local capacity = 10
32+
local sack = knapsack.fractional(items, capacity)
33+
assert(#sack == 3)
34+
local s1, s2, s3 = sack[1], sack[2], sack[3]
35+
print('s3', s3.w-1)
36+
assert(s1.name == 'Rice' and s1.p == 1 and s1.w == 5 and s1.b == 6)
37+
assert(s2.name == 'Orange' and s2.p == 1 and s2.w == 4 and s2.b == 5)
38+
assert(s3.name == 'Apple' and fuzzyEq(s3.p,0.33) and s3.w == 1 and fuzzyEq(s3.b,1.67))
39+
end)
40+
41+
42+
print(('-'):rep(80))
43+
print(('Total : %02d: Pass: %02d - Failed : %02d - Success: %.2f %%')
44+
:format(total, pass, total-pass, (pass*100/total)))

0 commit comments

Comments
 (0)