RosettaCodeData/Task/Vogels-approximation-method/Zig/vogels-approximation-method...

213 lines
6.2 KiB
Zig

const std = @import("std");
const print = std.debug.print;
const ArrayList = std.ArrayList;
const Vogel = struct {
supply: ArrayList(i32),
demand: ArrayList(i32),
costs: ArrayList(ArrayList(i32)),
n_rows: i32,
n_cols: i32,
row_done: ArrayList(bool),
col_done: ArrayList(bool),
allocator: std.mem.Allocator,
const Self = @This();
pub fn init(allocator: std.mem.Allocator) Self {
return Self{
.supply = ArrayList(i32).init(allocator),
.demand = ArrayList(i32).init(allocator),
.costs = ArrayList(ArrayList(i32)).init(allocator),
.n_rows = 0,
.n_cols = 0,
.row_done = ArrayList(bool).init(allocator),
.col_done = ArrayList(bool).init(allocator),
.allocator = allocator,
};
}
pub fn deinit(self: *Self) void {
self.supply.deinit();
self.demand.deinit();
for (self.costs.items) |*row| {
row.deinit();
}
self.costs.deinit();
self.row_done.deinit();
self.col_done.deinit();
}
pub fn approximate(self: *Self) !void {
var results = ArrayList(ArrayList(i32)).init(self.allocator);
defer {
for (results.items) |*row| {
row.deinit();
}
results.deinit();
}
// Initialize results matrix
for (0..@intCast(self.n_rows)) |_| {
var row = ArrayList(i32).init(self.allocator);
for (0..@intCast(self.n_cols)) |_| {
try row.append(0);
}
try results.append(row);
}
var supply_remaining: i32 = 0;
for (self.supply.items) |s| {
supply_remaining += s;
}
var total_cost: i32 = 0;
while (supply_remaining > 0) {
const cell = try self.next_cell();
const r = cell[0];
const c = cell[1];
const q = if (self.demand.items[c] < self.supply.items[r])
self.demand.items[c]
else
self.supply.items[r];
self.demand.items[c] -= q;
if (self.demand.items[c] == 0) {
self.col_done.items[c] = true;
}
self.supply.items[r] -= q;
if (self.supply.items[r] == 0) {
self.row_done.items[r] = true;
}
results.items[r].items[c] = q;
supply_remaining -= q;
total_cost += q * self.costs.items[r].items[c];
}
print(" A B C D E\n", .{});
for (results.items, 0..) |result, i| {
print("{c}", .{@as(u8, @intCast('W' + i))});
for (result.items) |item| {
print(" {:>2}", .{item});
}
print("\n", .{});
}
print("\nTotal Cost = {}\n", .{total_cost});
}
fn next_cell(self: *Self) ![]usize {
const res1 = try self.max_penalty(self.n_rows, self.n_cols, true);
const res2 = try self.max_penalty(self.n_cols, self.n_rows, false);
if (res1[3] == res2[3]) {
return if (res1[2] < res2[2]) res1 else res2;
}
return if (res1[3] > res2[3]) res2 else res1;
}
fn max_penalty(self: *Self, len1: i32, len2: i32, is_row: bool) ![]usize {
var md: i32 = std.math.minInt(i32);
var pc: i32 = -1;
var pm: i32 = -1;
var mc: i32 = -1;
for (0..@intCast(len1)) |i| {
const should_process = if (is_row)
!self.row_done.items[i]
else
!self.col_done.items[i];
if (should_process) {
var min1: i32 = std.math.maxInt(i32);
var min2: i32 = min1;
var min_p: i32 = -1;
for (0..@intCast(len2)) |j| {
const should_process_inner = if (is_row)
!self.col_done.items[j]
else
!self.row_done.items[j];
if (should_process_inner) {
const c = if (is_row)
self.costs.items[i].items[j]
else
self.costs.items[j].items[i];
if (c < min1) {
min2 = min1;
min1 = c;
min_p = @intCast(j);
} else if (c < min2) {
min2 = c;
}
}
}
const diff = min2 - min1;
if (diff > md) {
md = diff;
pm = @intCast(i);
mc = min1;
pc = min_p;
}
}
}
var result = try self.allocator.alloc(usize, 4);
if (is_row) {
result[0] = @intCast(pm);
result[1] = @intCast(pc);
result[2] = @intCast(mc);
result[3] = @intCast(md);
} else {
result[0] = @intCast(pc);
result[1] = @intCast(pm);
result[2] = @intCast(mc);
result[3] = @intCast(md);
}
return result;
}
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var my_test = Vogel.init(allocator);
defer my_test.deinit();
// Initialize supply
try my_test.supply.appendSlice(&[_]i32{ 50, 60, 50, 50 });
// Initialize demand
try my_test.demand.appendSlice(&[_]i32{ 30, 20, 70, 30, 60 });
// Initialize costs matrix
const cost_data = [_][]const i32{
&[_]i32{ 16, 16, 13, 22, 17 },
&[_]i32{ 14, 14, 13, 19, 15 },
&[_]i32{ 19, 19, 20, 23, 50 },
&[_]i32{ 50, 12, 50, 15, 11 },
};
for (cost_data) |row_data| {
var row = ArrayList(i32).init(allocator);
try row.appendSlice(row_data);
try my_test.costs.append(row);
}
my_test.n_rows = 4;
my_test.n_cols = 5;
// Initialize row_done and col_done
try my_test.row_done.appendNTimes(false, 4);
try my_test.col_done.appendNTimes(false, 5);
try my_test.approximate();
}