From fd580a184aa131b0e00a54a3cc3c356f38d7fade Mon Sep 17 00:00:00 2001 From: khoaxuantu <68913255+khoaxuantu@users.noreply.github.com> Date: Wed, 3 Apr 2024 04:10:25 +0700 Subject: [PATCH] feat: Add Ruby code - chapter "computational complexity" (#1212) * feat: add ruby code - chapter computational complexity * feat: add ruby code blocks --- .../iteration.rb | 78 +++++++++ .../recursion.rb | 69 ++++++++ .../space_complexity.rb | 91 ++++++++++ .../time_complexity.rb | 164 ++++++++++++++++++ .../worst_best_time_complexity.rb | 34 ++++ codes/ruby/utils/print_util.rb | 43 +++++ codes/ruby/utils/tree_node.rb | 18 ++ .../space_complexity.md | 45 ++++- .../time_complexity.md | 45 ++++- 9 files changed, 582 insertions(+), 5 deletions(-) create mode 100644 codes/ruby/chapter_computational_complexity/iteration.rb create mode 100644 codes/ruby/chapter_computational_complexity/recursion.rb create mode 100644 codes/ruby/chapter_computational_complexity/space_complexity.rb create mode 100644 codes/ruby/chapter_computational_complexity/time_complexity.rb create mode 100644 codes/ruby/chapter_computational_complexity/worst_best_time_complexity.rb create mode 100644 codes/ruby/utils/tree_node.rb diff --git a/codes/ruby/chapter_computational_complexity/iteration.rb b/codes/ruby/chapter_computational_complexity/iteration.rb new file mode 100644 index 000000000..43b227796 --- /dev/null +++ b/codes/ruby/chapter_computational_complexity/iteration.rb @@ -0,0 +1,78 @@ +=begin +File: iteration.rb +Created Time: 2024-03-30 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### for 循环 ### +def for_loop(n) + res = 0 + + # 循环求和 1, 2, ..., n-1, n + for i in 1..n + res += i + end + + res +end + +### while 循环 ### +def while_loop(n) + res = 0 + i = 1 # 初始化条件变量 + + # 循环求和 1, 2, ..., n-1, n + while i <= n + res += i + i += 1 # 更新条件变量 + end + + res +end + +### while 循环(两次更新)### +def while_loop_ii(n) + res = 0 + i = 1 # 初始化条件变量 + + # 循环求和 1, 4, 10, ... + while i <= n + res += i + # 更新条件变量 + i += 1 + i *= 2 + end + + res +end + +### 双层 for 循环 ### +def nested_for_loop(n) + res = "" + + # 循环 i = 1, 2, ..., n-1, n + for i in 1..n + # 循环 j = 1, 2, ..., n-1, n + for j in 1..n + res += "(#{i}, #{j}), " + end + end + + res +end + +### Driver Code ### + +n = 5 + +res = for_loop n +puts "\nfor 循环的求和结果 res = #{res}" + +res = while_loop n +puts "\nwhile 循环的求和结果 res = #{res}" + +res = while_loop_ii n +puts "\nwhile 循环(两次更新)求和结果 res = #{res}" + +res = nested_for_loop n +puts "\n双层 for 循环的遍历结果 #{res}" diff --git a/codes/ruby/chapter_computational_complexity/recursion.rb b/codes/ruby/chapter_computational_complexity/recursion.rb new file mode 100644 index 000000000..6fae30851 --- /dev/null +++ b/codes/ruby/chapter_computational_complexity/recursion.rb @@ -0,0 +1,69 @@ +=begin +File: recursion.rb +Created Time: 2024-03-30 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 递归 ### +def recur(n) + # 终止条件 + return 1 if n == 1 + # 递:递归调用 + res = recur n - 1 + # 归:返回结果 + n + res +end + +### 使用迭代模拟递归 ### +def for_loop_recur(n) + # 使用一个显式的栈来模拟系统调用栈 + stack = [] + res = 0 + + # 递:递归调用 + for i in n.downto(0) + # 通过“入栈操作”模拟“递” + stack << i + end + # 归:返回结果 + while !stack.empty? + res += stack.pop + end + + # res = 1+2+3+...+n + res +end + +### 尾递归 ### +def tail_recur(n, res) + # 终止条件 + return res if n == 0 + # 尾递归调用 + tail_recur n - 1, res + n +end + +### 斐波那契数列:递归 ### +def fib(n) + # 终止条件 f(1) = 0, f(2) = 1 + return n - 1 if n == 1 || n == 2 + # 递归调用 f(n) = f(n-1) + f(n-2) + res = fib(n - 1) + fib(n - 2) + # 返回结果 f(n) + res +end + +### Driver Code ### + +n = 5 + +res = recur n +puts "\n递归函数的求和结果 res = #{res}" + +res = for_loop_recur n +puts "\n使用迭代模拟递归求和结果 res = #{res}" + +res = tail_recur n, 0 +puts "\n尾递归函数的求和结果 res = #{res}" + +res = fib n +puts "\n斐波那契数列的第 #{n} 项为 #{res}" diff --git a/codes/ruby/chapter_computational_complexity/space_complexity.rb b/codes/ruby/chapter_computational_complexity/space_complexity.rb new file mode 100644 index 000000000..cad8f530d --- /dev/null +++ b/codes/ruby/chapter_computational_complexity/space_complexity.rb @@ -0,0 +1,91 @@ +=begin +File: space_complexity.rb +Created Time: 2024-03-30 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/list_node' +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### 函数 ### +def function + # 执行某些操作 + 0 +end + +### 常数阶 ### +def constant(n) + # 常量、变量、对象占用 O(1) 空间 + a = 0 + nums = [0] * 10000 + node = ListNode.new + + # 循环中的变量占用 O(1) 空间 + (0...n).each { c = 0 } + # 循环中的函数占用 O(1) 空间 + (0...n).each { function } +end + +### 线性阶 ### +def linear(n) + # 长度为 n 的列表占用 O(n) 空间 + nums = Array.new n, 0 + + # 长度为 n 的哈希表占用 O(n) 空间 + hmap = {} + for i in 0...n + hmap[i] = i.to_s + end +end + +### 线性阶(递归实现)### +def linear_recur(n) + puts "递归 n = #{n}" + return if n == 1 + linear_recur n - 1 +end + +### 平方阶 ### +def quadratic(n) + # 二维列表占用 O(n^2) 空间 + Array.new(n) { Array.new n, 0 } +end + +### 平方阶(递归实现)### +def quadratic_recur(n) + return 0 unless n > 0 + + # 数组 nums 长度为 n, n-1, ..., 2, 1 + nums = Array.new n, 0 + quadratic_recur n - 1 +end + +### 指数阶(建立满二叉树)### +def build_tree(n) + return if n == 0 + + TreeNode.new.tap do |root| + root.left = build_tree n - 1 + root.right = build_tree n - 1 + end +end + +### Driver Code ### + +n = 5 + +# 常数阶 +constant n + +# 线性阶 +linear n +linear_recur n + +# 平方阶 +quadratic n +quadratic_recur n + +# 指数阶 +root = build_tree n +print_tree root diff --git a/codes/ruby/chapter_computational_complexity/time_complexity.rb b/codes/ruby/chapter_computational_complexity/time_complexity.rb new file mode 100644 index 000000000..105ce9346 --- /dev/null +++ b/codes/ruby/chapter_computational_complexity/time_complexity.rb @@ -0,0 +1,164 @@ +=begin +File: time_complexity.rb +Created Time: 2024-03-30 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 常数阶 ### +def constant(n) + count = 0 + size = 100000 + + (0...size).each { count += 1 } + + count +end + +### 线性阶 ### +def linear(n) + count = 0 + (0...n).each { count += 1 } + count +end + +### 线性阶(遍历数组)### +def array_traversal(nums) + count = 0 + + # 循环次数与数组长度成正比 + for num in nums + count += 1 + end + + count +end + +### 平方阶 ### +def quadratic(n) + count = 0 + + # 循环次数与数据大小 n 成平方关系 + for i in 0...n + for j in 0...n + count += 1 + end + end + + count +end + +### 平方阶(冒泡排序)### +def bubble_sort(nums) + count = 0 # 计数器 + + # 外循环:未排序区间为 [0, i] + for i in (nums.length - 1).downto(0) + # 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端 + for j in 0...i + if nums[j] > nums[j + 1] + # 交换 nums[j] 与 nums[j + 1] + tmp = nums[j] + nums[j] = nums[j + 1] + nums[j + 1] = tmp + count += 3 # 元素交换包含 3 个单元操作 + end + end + end + + count +end + +### 指数阶(循环实现)### +def exponential(n) + count, base = 0, 1 + + # 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + (0...n).each do + (0...base).each { count += 1 } + base *= 2 + end + + # count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + count +end + +### 指数阶(递归实现)### +def exp_recur(n) + return 1 if n == 1 + exp_recur(n - 1) + exp_recur(n - 1) + 1 +end + +### 对数阶(循环实现)### +def logarithmic(n) + count = 0 + + while n > 1 + n /= 2 + count += 1 + end + + count +end + +### 对数阶(递归实现)### +def log_recur(n) + return 0 unless n > 1 + log_recur(n / 2) + 1 +end + +### 线性对数阶 +def linear_log_recur(n) + return 1 unless n > 1 + + count = linear_log_recur(n / 2) + linear_log_recur(n / 2) + (0...n).each { count += 1 } + + count +end + +### 阶乘阶(递归实现)### +def factorial_recur(n) + return 1 if n == 0 + + count = 0 + # 从 1 个分裂出 n 个 + (0...n).each { count += factorial_recur(n - 1) } + + count +end + +### Driver Code ### + +# 可以修改 n 运行,体会一下各种复杂度的操作数量变化趋势 +n = 8 +puts "输入数据大小 n = #{n}" + +count = constant n +puts "常数阶的操作数量 = #{count}" + +count = linear n +puts "线性阶的操作数量 = #{count}" +count = array_traversal Array.new n, 0 +puts "线性阶(遍历数组)的操作数量 = #{count}" + +count = quadratic n +puts "平方阶的操作数量 = #{count}" +nums = Array.new(n) { |i| n - i } # [n, n-1, ..., 2, 1] +count = bubble_sort nums +puts "平方阶(冒泡排序)的操作数量 = #{count}" + +count = exponential n +puts "指数阶(循环实现)的操作数量 = #{count}" +count = exp_recur n +puts "指数阶(递归实现)的操作数量 = #{count}" + +count = logarithmic n +puts "对数阶(循环实现)的操作数量 = #{count}" +count = log_recur n +puts "对数阶(递归实现)的操作数量 = #{count}" + +count = linear_log_recur n +puts "线性对数阶(递归实现)的操作数量 = #{count}" + +count = factorial_recur n +puts "阶乘阶(递归实现)的操作数量 = #{count}" diff --git a/codes/ruby/chapter_computational_complexity/worst_best_time_complexity.rb b/codes/ruby/chapter_computational_complexity/worst_best_time_complexity.rb new file mode 100644 index 000000000..eef3e3ae5 --- /dev/null +++ b/codes/ruby/chapter_computational_complexity/worst_best_time_complexity.rb @@ -0,0 +1,34 @@ +=begin +File: worst_best_time_complexity.rb +Created Time: 2024-03-30 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 生成一个数组,元素为: 1, 2, ..., n ,顺序被打乱 ### +def random_number(n) + # 生成数组 nums =: 1, 2, 3, ..., n + nums = Array.new(n) { |i| i + 1 } + # 随机打乱数组元素 + nums.shuffle! +end + +### 查找数组 nums 中数字 1 所在索引 ### +def find_one(nums) + for i in 0...nums.length + # 当元素 1 在数组头部时,达到最佳时间复杂度 O(1) + # 当元素 1 在数组尾部时,达到最差时间复杂度 O(n) + return i if nums[i] == 1 + end + + -1 +end + +### Driver Code ### + +for i in 0...10 + n = 100 + nums = random_number n + index = find_one nums + puts "\n数组 [ 1, 2, ..., n ] 被打乱后 = #{nums}" + puts "数字 1 的索引为 #{index}" +end diff --git a/codes/ruby/utils/print_util.rb b/codes/ruby/utils/print_util.rb index 87c1fc7e3..dee5c7bbe 100644 --- a/codes/ruby/utils/print_util.rb +++ b/codes/ruby/utils/print_util.rb @@ -13,3 +13,46 @@ def print_linked_list(head) end puts "#{list.join(" -> ")}" end + +class Trunk + attr_accessor :prev, :str + + def initialize(prev, str) + @prev = prev + @str = str + end +end + +def show_trunk(p) + return if p.nil? + + show_trunk(p.prev) + print p.str +end + +### 打印二叉树 ### +# This tree printer is borrowed from TECHIE DELIGHT +# https://www.techiedelight.com/c-program-print-binary-tree/ +def print_tree(root, prev=nil, is_right=false) + return if root.nil? + + prev_str = " " + trunk = Trunk.new prev, prev_str + print_tree root.right, trunk, true + + if prev.nil? + trunk.str = "———" + elsif is_right + trunk.str = "/———" + prev_str = " |" + else + trunk.str = "\\———" + prev.str = prev_str + end + + show_trunk trunk + puts " #{root.val}" + prev.str = prev_str if prev + trunk.str = " |" + print_tree root.left, trunk, false +end diff --git a/codes/ruby/utils/tree_node.rb b/codes/ruby/utils/tree_node.rb new file mode 100644 index 000000000..da796afb2 --- /dev/null +++ b/codes/ruby/utils/tree_node.rb @@ -0,0 +1,18 @@ +=begin +File: tree_node.rb +Created Time: 2024-03-30 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 二叉树节点类 ### +class TreeNode + attr_accessor :val # 节点值 + attr_accessor :height # 节点高度 + attr_accessor :left # 左子节点引用 + attr_accessor :right # 右子节点引用 + + def initialize(val=0) + @val = val + @height = 0 + end +end diff --git a/docs/chapter_computational_complexity/space_complexity.md b/docs/chapter_computational_complexity/space_complexity.md index a2d76f215..d2665ae6b 100755 --- a/docs/chapter_computational_complexity/space_complexity.md +++ b/docs/chapter_computational_complexity/space_complexity.md @@ -335,7 +335,30 @@ === "Ruby" ```ruby title="" + ### 类 ### + class Node + attr_accessor :val # 节点值 + attr_accessor :next # 指向下一节点的引用 + + def initialize(x) + @val = x + end + end + + ### 函数 ### + def function + # 执行某些操作... + 0 + end + ### 算法 ### + def algorithm(n) # 输入数据 + a = 0 # 暂存数据(常量) + b = 0 # 暂存数据(变量) + node = Node.new 0 # 暂存数据(对象) + c = function # 栈帧空间(调用函数) + a + b + c # 输出数据 + end ``` === "Zig" @@ -499,7 +522,11 @@ === "Ruby" ```ruby title="" - + def algorithm(n) + a = 0 # O(1) + b = Array.new 10000 # O(1) + nums = Array.new n if n > 10 # O(n) + end ``` === "Zig" @@ -763,7 +790,21 @@ === "Ruby" ```ruby title="" - + def function + # 执行某些操作 + 0 + end + + ### 循环的空间复杂度为 O(1) ### + def loop(n) + (0...n).each { function } + end + + ### 递归的空间复杂度为 O(n) ### + def recur(n) + return if n == 1 + recur n - 1 + end ``` === "Zig" diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index 55cdb25cc..c1edeef81 100755 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -189,7 +189,16 @@ === "Ruby" ```ruby title="" - + # 在某运行平台下 + def algorithm(n) + a = 2 # 1 ns + a = a + 1 # 1 ns + a = a * 2 # 10 ns + # 循环 n 次 + (n...0).each do # 1 ns + puts 0 # 5 ns + end + end ``` === "Zig" @@ -474,7 +483,20 @@ $$ === "Ruby" ```ruby title="" + # 算法 A 的时间复杂度:常数阶 + def algorithm_A(n) + puts 0 + end + + # 算法 B 的时间复杂度:线性阶 + def algorithm_B(n) + (0...n).each { puts 0 } + end + # 算法 C 的时间复杂度:常数阶 + def algorithm_C(n) + (0...1_000_000).each { puts 0 } + end ``` === "Zig" @@ -688,7 +710,15 @@ $$ === "Ruby" ```ruby title="" - + def algorithm(n) + a = 1 # +1 + a = a + 1 # +1 + a = a * 2 # +1 + # 循环 n 次 + (0...n).each do # +1 + puts 0 # +1 + end + end ``` === "Zig" @@ -970,7 +1000,16 @@ $T(n)$ 是一次函数,说明其运行时间的增长趋势是线性的,因 === "Ruby" ```ruby title="" - + def algorithm(n) + a = 1 # +0(技巧 1) + a = a + n # +0(技巧 1) + # +n(技巧 2) + (0...(5 * n + 1)).each do { puts 0 } + # +n*n(技巧 3) + (0...(2 * n)).each do + (0...(n + 1)).each do { puts 0 } + end + end ``` === "Zig"