From 2035aa0cf1f8c4cac395bb83437350c5e013b95b Mon Sep 17 00:00:00 2001 From: Spark Date: Tue, 24 Oct 2023 11:49:13 -0400 Subject: [PATCH] feat: iteration & recursion in Zig (#804) * iteration & recursion in Zig * missing part in time_complexity.md (zig) * build.zig sync * Update recursion.zig * Update iteration.zig --------- Co-authored-by: Yudong Jin --- codes/zig/build.zig | 8 ++ .../iteration.zig | 77 ++++++++++++++++++ .../recursion.zig | 78 +++++++++++++++++++ .../time_complexity.md | 55 ++++++++++++- 4 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 codes/zig/chapter_computational_complexity/iteration.zig create mode 100644 codes/zig/chapter_computational_complexity/recursion.zig diff --git a/codes/zig/build.zig b/codes/zig/build.zig index e030d9f6f..9c77e0e6f 100644 --- a/codes/zig/build.zig +++ b/codes/zig/build.zig @@ -24,6 +24,14 @@ pub fn build(b: *std.Build) void { // Run Command: zig build run_space_complexity -Doptimize=ReleaseSafe .{ .name = "space_complexity", .path = "chapter_computational_complexity/space_complexity.zig" }, + // Source File: "chapter_computational_complexity/iteration.zig" + // Run Command: zig build run_iteration -Doptimize=ReleaseFast + .{ .name = "iteration", .path = "chapter_computational_complexity/iteration.zig" }, + + // Source File: "chapter_computational_complexity/recursion.zig" + // Run Command: zig build run_recursion -Doptimize=ReleaseFast + .{ .name = "recursion", .path = "chapter_computational_complexity/recursion.zig" }, + // Source File: "chapter_array_and_linkedlist/array.zig" // Run Command: zig build run_array -Doptimize=ReleaseSafe .{ .name = "array", .path = "chapter_array_and_linkedlist/array.zig" }, diff --git a/codes/zig/chapter_computational_complexity/iteration.zig b/codes/zig/chapter_computational_complexity/iteration.zig new file mode 100644 index 000000000..23866c105 --- /dev/null +++ b/codes/zig/chapter_computational_complexity/iteration.zig @@ -0,0 +1,77 @@ +// File: iteration.zig +// Created Time: 2023-09-27 +// Author: QiLOL (pikaqqpika@gmail.com) + +const std = @import("std"); +const Allocator = std.mem.Allocator; + +// for 循环 +fn forLoop(n: usize) i32 { + var res: i32 = 0; + // 循环求和 1, 2, ..., n-1, n + for (1..n+1) |i| { + res = res + @as(i32, @intCast(i)); + } + return res; +} + +// while 循环 +fn whileLoop(n: i32) i32 { + var res: i32 = 0; + var i: i32 = 1; // 初始化条件变量 + // 循环求和 1, 2, ..., n-1, n + while (i <= n) { + res += @intCast(i); + i += 1; + } + return res; +} + +// while 循环(两次更新) +fn whileLoopII(n: i32) i32 { + var res: i32 = 0; + var i: i32 = 1; // 初始化条件变量 + // 循环求和 1, 4, ... + while (i <= n) { + res += @intCast(i); + // 更新条件变量 + i += 1; + i *= 2; + } + return res; +} + +// 双层 for 循环 +fn nestedForLoop(allocator: Allocator, n: usize) ![]const u8 { + var res = std.ArrayList(u8).init(allocator); + defer res.deinit(); + var buffer: [20]u8 = undefined; + // 循环 i = 1, 2, ..., n-1, n + for (1..n+1) |i| { + // 循环 j = 1, 2, ..., n-1, n + for (1..n+1) |j| { + var _str = try std.fmt.bufPrint(&buffer, "({d}, {d}), ", .{i, j}); + try res.appendSlice(_str); + } + } + return res.toOwnedSlice(); +} + +// Driver Code +pub fn main() !void { + const n: i32 = 5; + var res: i32 = 0; + + res = forLoop(n); + std.debug.print("\nfor 循环的求和结果 res = {}\n", .{res}); + + res = whileLoop(n); + std.debug.print("\nwhile 循环的求和结果 res = {}\n", .{res}); + + res = whileLoopII(n); + std.debug.print("\nwhile 循环(两次更新)求和结果 res = {}\n", .{res}); + + const allocator = std.heap.page_allocator; + const resStr = try nestedForLoop(allocator, n); + std.debug.print("\n双层 for 循环的遍历结果 {s}\n", .{resStr}); +} diff --git a/codes/zig/chapter_computational_complexity/recursion.zig b/codes/zig/chapter_computational_complexity/recursion.zig new file mode 100644 index 000000000..d0f586c78 --- /dev/null +++ b/codes/zig/chapter_computational_complexity/recursion.zig @@ -0,0 +1,78 @@ +// File: recursion.zig +// Created Time: 2023-09-27 +// Author: QiLOL (pikaqqpika@gmail.com) + +const std = @import("std"); + +// 递归函数 +fn recur(n: i32) i32 { + // 终止条件 + if (n == 1) { + return 1; + } + // 递:递归调用 + var res: i32 = recur(n - 1); + // 归:返回结果 + return n + res; +} + +// 使用迭代模拟递归 +fn forLoopRecur(comptime n: i32) i32 { + // 使用一个显式的栈来模拟系统调用栈 + var stack: [n]i32 = undefined; + var res: i32 = 0; + // 递:递归调用 + var i: usize = n; + while (i > 0) { + stack[i - 1] = @intCast(i); + i -= 1; + } + // 归:返回结果 + var index: usize = n; + while (index > 0) { + index -= 1; + res += stack[index]; + } + // res = 1+2+3+...+n + return res; +} + +// 尾递归函数 +fn tailRecur(n: i32, res: i32) i32 { + // 终止条件 + if (n == 0) { + return res; + } + // 尾递归调用 + return tailRecur(n - 1, res + n); +} + +// 斐波那契数列 +fn fib(n: i32) i32 { + // 终止条件 f(1) = 0, f(2) = 1 + if (n == 1 or n == 2) { + return n - 1; + } + // 递归调用 f(n) = f(n-1) + f(n-2) + var res: i32 = fib(n - 1) + fib(n - 2); + // 返回结果 f(n) + return res; +} + +// Driver Code +pub fn main() !void { + const n: i32 = 5; + var res: i32 = 0; + + res = recur(n); + std.debug.print("\n递归函数的求和结果 res = {}\n", .{recur(n)}); + + res = forLoopRecur(n); + std.debug.print("\n使用迭代模拟递归的求和结果 res = {}\n", .{forLoopRecur(n)}); + + res = tailRecur(n, 0); + std.debug.print("\n尾递归函数的求和结果 res = {}\n", .{tailRecur(n, 0)}); + + res = fib(n); + std.debug.print("\n斐波那契数列的第 {} 项为 {}\n", .{n, fib(n)}); +} diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index b71cd74a3..a2cbaf206 100755 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -174,7 +174,16 @@ === "Zig" ```zig title="" - + // 在某运行平台下 + fn algorithm(n: usize) void { + var a: i32 = 2; // 1 ns + a += 1; // 1 ns + a *= 2; // 10 ns + // 循环 n 次 + for (0..n) |_| { // 1 ns + std.debug.print("{}\n", .{0}); // 5 ns + } + } ``` 根据以上方法,可以得到算法运行时间为 $6n + 12$ ns : @@ -423,7 +432,24 @@ $$ === "Zig" ```zig title="" - + // 算法 A 的时间复杂度:常数阶 + fn algorithm_A(n: usize) void { + _ = n; + std.debug.print("{}\n", .{0}); + } + // 算法 B 的时间复杂度:线性阶 + fn algorithm_B(n: i32) void { + for (0..n) |_| { + std.debug.print("{}\n", .{0}); + } + } + // 算法 C 的时间复杂度:常数阶 + fn algorithm_C(n: i32) void { + _ = n; + for (0..1000000) |_| { + std.debug.print("{}\n", .{0}); + } + } ``` 下图展示了以上三个算法函数的时间复杂度。 @@ -600,7 +626,15 @@ $$ === "Zig" ```zig title="" - + fn algorithm(n: usize) void { + var a: i32 = 1; // +1 + a += 1; // +1 + a *= 2; // +1 + // 循环 n 次 + for (0..n) |_| { // +1(每轮都执行 i ++) + std.debug.print("{}\n", .{0}); // +1 + } + } ``` 设算法的操作数量是一个关于输入数据大小 $n$ 的函数,记为 $T(n)$ ,则以上函数的的操作数量为: @@ -849,7 +883,22 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因 === "Zig" ```zig title="" + fn algorithm(n: usize) void { + var a: i32 = 1; // +0(技巧 1) + a = a + @as(i32, @intCast(n)); // +0(技巧 1) + // +n(技巧 2) + for(0..(5 * n + 1)) |_| { + std.debug.print("{}\n", .{0}); + } + + // +n*n(技巧 3) + for(0..(2 * n)) |_| { + for(0..(n + 1)) |_| { + std.debug.print("{}\n", .{0}); + } + } + } ``` 以下公式展示了使用上述技巧前后的统计结果,两者推出的时间复杂度都为 $O(n^2)$ 。