Computing Euclidean Distances in Zig
This program calculates the Euclidean distance between two randomly generated vectors of 64-bit floats—think of it as measuring the straight-line distance in multi-dimensional space, a fundamental concept in machine learning, data analysis and graphics.
const std = @import("std");
fn euclideanDistance(a: []const f64, b: []const f64) !f64 {
if (a.len != b.len) return error.MismatchedLengths;
var sum: f64 = 0.0;
for (a, b) |x, y| {
const diff = x - y;
sum += diff * diff;
}
return @sqrt(sum);
}
pub fn main() !void {
const allocator = std.heap.page_allocator;
const length: usize = 32;
const ts1 = try allocator.alloc(f64, length);
defer allocator.free(ts1);
const ts2 = try allocator.alloc(f64, length);
defer allocator.free(ts2);
var rng = std.crypto.random;
for (ts1) |*val| {
val.* = rng.float(f64);
}
for (ts2) |*val| {
val.* = rng.float(f64);
}
const distance = try euclideanDistance(ts1, ts2);
std.debug.print("Euclidean distance = {d:.4}\n", .{distance});
}
This Zig code demonstrates the calculation of the Euclidean distance between two vectors represented as slices of 64-bit floating-point numbers (f64). It consists of a function to compute the distance and a main function that generates random vectors, computes their distance and prints the result. The code uses Zig’s standard library for memory allocation, random number generation, and debugging output.
The code begins by importing the standard library with const std = @import("std");. This provides access to essential utilities like heap allocation and cryptographic random number generation.
Next, the euclideanDistance() function is defined. It takes two constant slices of f64 as input: a and b. The function first checks if the lengths of the slices are equal using if (a.len != b.len) return error.MismatchedLengths;. If they mismatch, it returns an error. Otherwise, it initializes a sum variable to 0.0 and iterates over the elements of both slices simultaneously with for (a, b) |x, y|. For each pair of elements, it calculates the difference (const diff = x - y;), squares it, and adds it to the sum. Finally, it returns the square root of the sum using @sqrt(sum), which represents the Euclidean distance.
The main() function is the entry point, marked as pub fn main() !void. It starts by setting up a page allocator from the standard library’s heap module: const allocator = std.heap.page_allocator;. It defines a constant length of 32 for the vectors. Two slices, ts1 and ts2, are allocated using try allocator.alloc(f64, length);, and deallocation is deferred with defer allocator.free(ts1); and similarly for ts2 to ensure memory is freed at the end of the scope.
A random number generator is initialized with var rng = std.crypto.random;. The code then fills ts1 and ts2 with random floating-point values between 0 and 1 using loops: for (ts1) |*val| { val.* = rng.float(f64); } and similarly for ts2. This uses pointer dereferencing (val.*) to assign values.
The Euclidean distance is computed by calling const distance = try euclideanDistance(ts1, ts2);, which may propagate an error if lengths mismatch, though in this case they are the same. Finally, the result is printed with four decimal places using std.debug.print("Euclidean distance = {d:.4}\n", .{distance});, where {d:.4} formats the float.
Save the code as ed.zig and execute it on your machine:
$ zig run ed.zig
Euclidean distance = 2.3077
Happy coding in Zig!