September Morn Update

This commit is contained in:
Ingy döt Net 2019-09-12 10:33:56 -07:00
parent 4e2d22a71d
commit aac6731f2c
6856 changed files with 141342 additions and 21127 deletions

View file

@ -0,0 +1,86 @@
require "bit_array"
struct BitArray
def clone
BitArray.new(size).tap { |new| new.to_slice.copy_from (to_slice) }
end
end
record Item, name : String, weight : Int32, value : Int32
record Selection, mask : BitArray, cur_index : Int32, total_value : Int32
class Knapsack
@threshold_value = 0
@threshold_choice : Selection?
getter checked_nodes = 0
def knapsack_step(taken, items, remaining_weight)
if taken.total_value > @threshold_value
@threshold_value = taken.total_value
@threshold_choice = taken
end
candidate_index = items.index(taken.cur_index) { |item| item.weight <= remaining_weight }
return nil unless candidate_index
@checked_nodes += 1
candidate = items[candidate_index]
# candidate is a best of available items, so if we fill remaining value with it
# and still don't reach the threshold, the branch is wrong
return nil if taken.total_value + 1.0 * candidate.value / candidate.weight * remaining_weight < @threshold_value
# now recursively check both variants
mask = taken.mask.clone
mask[candidate_index] = true
knapsack_step Selection.new(mask, candidate_index + 1, taken.total_value + candidate.value), items, remaining_weight - candidate.weight
mask = taken.mask.clone
mask[candidate_index] = false
knapsack_step Selection.new(mask, candidate_index + 1, taken.total_value), items, remaining_weight
end
def select(items, max_weight)
@checked_variants = 0
# sort by descending relative value
list = items.sort_by { |item| -1.0 * item.value / item.weight }
# use heuristic of relative value as an initial estimate for branch&bounds
w = max_weight
heur_list = list.take_while { |item| w -= item.weight; w > 0 }
nothing = Selection.new(BitArray.new(items.size), 0, 0)
@threshold_value = heur_list.sum(&.value) - 1
@threshold_choice = nothing
knapsack_step(nothing, list, max_weight)
selected = @threshold_choice.not_nil!
result = [] of Item
selected.mask.each_with_index { |v, i| result << list[i] if v }
result
end
end
possible = [
Item.new("map", 9, 150),
Item.new("compass", 13, 35),
Item.new("water", 153, 200),
Item.new("sandwich", 50, 160),
Item.new("glucose", 15, 60),
Item.new("tin", 68, 45),
Item.new("banana", 27, 60),
Item.new("apple", 39, 40),
Item.new("cheese", 23, 30),
Item.new("beer", 52, 10),
Item.new("suntan cream", 11, 70),
Item.new("camera", 32, 30),
Item.new("T-shirt", 24, 15),
Item.new("trousers", 48, 10),
Item.new("umbrella", 73, 40),
Item.new("waterproof trousers", 42, 70),
Item.new("waterproof overclothes", 43, 75),
Item.new("note-case", 22, 80),
Item.new("sunglasses", 7, 20),
Item.new("towel", 18, 12),
Item.new("socks", 4, 50),
Item.new("book", 30, 10),
]
solver = Knapsack.new
used = solver.select(possible, 400)
puts "optimal choice: #{used.map(&.name)}"
puts "total weight #{used.sum(&.weight)}, total value #{used.sum(&.value)}"
puts "checked nodes: #{solver.checked_nodes}"

View file

@ -0,0 +1,57 @@
my $raw = qq:to/TABLE/;
map 9 150
compass 13 35
water 153 200
sandwich 50 160
glucose 15 60
tin 68 45
banana 27 60
apple 39 40
cheese 23 30
beer 52 10
suntancream 11 70
camera 32 30
T-shirt 24 15
trousers 48 10
umbrella 73 40
waterproof trousers 42 70
waterproof overclothes 43 75
note-case 22 80
sunglasses 7 20
towel 18 12
socks 4 50
book 30 10
TABLE
my (@name, @weight, @value);
for split(["\n", /\t+/], $raw, :skip-empty) -> $n,$w,$v {
@name.push: $n;
@weight.push: $w;
@value.push: $v;
}
my $max_weight = 400;
my @p = [Nil xx $max_weight+2 ] xx @name;
sub optimal ($i, $w) {
return [0, []] if $i < 0;
return |@p[$i][$w] if @p[$i][$w];
if @weight[$i] > $w {
@p[$i][$w] = optimal($i-1, $w);
} else {
my @x = optimal($i-1, $w);
my @y = optimal($i-1, $w - @weight[$i]);
if (@x[0] > @y[0] + @value[$i] ) {
@p[$i][$w] = @x;
} else {
@p[$i][$w] = @y[0] + @value[$i], [|@y[1], @name[$i]];
}
}
return |@p[$i][$w]
}
my @solution = optimal(-1+@name, $max_weight);
say "@solution[0]: " ~ join ' ', @solution[1];

View file

@ -25,41 +25,35 @@ TABLE
my (@name, @weight, @value);
for (split "\n", $raw) {
for ([ split /\t+/ ]) {
push @name, $_->[0];
push @weight, $_->[1];
push @value, $_->[2];
}
for ([ split /\t+/ ]) {
push @name, $_->[0];
push @weight, $_->[1];
push @value, $_->[2];
}
}
my $max_weight = 400;
my @p = (map([[0, []], map undef, 0 .. $max_weight], 0 .. $#name),
[ map([0, []], 0 .. $max_weight + 1)]);
my @p = map [map undef, 0 .. 1+$max_weight], 0 .. $#name;
sub optimal {
my ($i, $w) = @_;
my ($i, $w) = @_;
return [0, []] if $i < 0;
return $p[$i][$w] if $p[$i][$w];
if (!defined $p[$i][$w]) {
if ($weight[$i] > $w) {
$p[$i][$w] = optimal($i - 1, $w)
} else {
my $x = optimal($i - 1, $w);
my $y = optimal($i - 1, $w - $weight[$i]);
if ($weight[$i] > $w) {
$p[$i][$w] = optimal($i - 1, $w)
} else {
my $x = optimal($i - 1, $w);
my $y = optimal($i - 1, $w - $weight[$i]);
if ($x->[0] > $y->[0] + $value[$i]) {
$p[$i][$w] = $x
} else {
$p[$i][$w] = [ $y->[0] + $value[$i],
[ @{$y->[1]}, $name[$i] ]]
}
}
if ($x->[0] > $y->[0] + $value[$i]) {
$p[$i][$w] = $x
} else {
$p[$i][$w] = [ $y->[0] + $value[$i], [ @{$y->[1]}, $name[$i] ]]
}
return $p[$i][$w]
}
return $p[$i][$w]
}
my $sol = optimal($#name, $max_weight);
print "$sol->[0]: @{$sol->[1]}\n";
# prints:
# 1030: map compass water sandwich glucose banana suntancream waterproof trousers
# waterproof overclothes note-case sunglasses socks
my $solution = optimal($#name, $max_weight);
print "$solution->[0]: @{$solution->[1]}\n";

View file

@ -0,0 +1,60 @@
struct KnapsackItem {
var name: String
var weight: Int
var value: Int
}
func knapsack(items: [KnapsackItem], limit: Int) -> [KnapsackItem] {
var table = Array(repeating: Array(repeating: 0, count: limit + 1), count: items.count + 1)
for j in 1..<items.count+1 {
let item = items[j-1]
for w in 1..<limit+1 {
if item.weight > w {
table[j][w] = table[j-1][w]
} else {
table[j][w] = max(table[j-1][w], table[j-1][w-item.weight] + item.value)
}
}
}
var result = [KnapsackItem]()
var w = limit
for j in stride(from: items.count, to: 0, by: -1) where table[j][w] != table[j-1][w] {
let item = items[j-1]
result.append(item)
w -= item.weight
}
return result
}
let items = [
KnapsackItem(name: "map", weight: 9, value: 150), KnapsackItem(name: "compass", weight: 13, value: 35),
KnapsackItem(name: "water", weight: 153, value: 200), KnapsackItem(name: "sandwich", weight: 50, value: 160),
KnapsackItem(name: "glucose", weight: 15, value: 60), KnapsackItem(name: "tin", weight: 68, value: 45),
KnapsackItem(name: "banana", weight: 27, value: 60), KnapsackItem(name: "apple", weight: 39, value: 40),
KnapsackItem(name: "cheese", weight: 23, value: 30), KnapsackItem(name: "beer", weight: 52, value: 10),
KnapsackItem(name: "suntan cream", weight: 11, value: 70), KnapsackItem(name: "camera", weight: 32, value: 30),
KnapsackItem(name: "t-shirt", weight: 24, value: 15), KnapsackItem(name: "trousers", weight: 48, value: 10),
KnapsackItem(name: "umbrella", weight: 73, value: 40), KnapsackItem(name: "waterproof trousers", weight: 42, value: 70),
KnapsackItem(name: "waterproof overclothes", weight: 43, value: 75), KnapsackItem(name: "note-case", weight: 22, value: 80),
KnapsackItem(name: "sunglasses", weight: 7, value: 20), KnapsackItem(name: "towel", weight: 18, value: 12),
KnapsackItem(name: "socks", weight: 4, value: 50), KnapsackItem(name: "book", weight: 30, value: 10)
]
let kept = knapsack(items: items, limit: 400)
print("Kept: ")
for item in kept {
print(" \(item.name)")
}
let (tValue, tWeight) = kept.reduce((0, 0), { ($0.0 + $1.value, $0.1 + $1.weight) })
print("For a total value of \(tValue) and a total weight of \(tWeight)")