diff --git a/contents/graham_scan/code/ruby/graham_scan.rb b/contents/graham_scan/code/ruby/graham_scan.rb new file mode 100644 index 000000000..804fb561e --- /dev/null +++ b/contents/graham_scan/code/ruby/graham_scan.rb @@ -0,0 +1,52 @@ +def graham_scan(points) + # First, sort the points so the one with the lowest y-coordinate comes first (the pivot) + points = points.sort_by { |point| point[:y] } + pivot = points[0] + + # Then sort all remaining points based on the angle between the pivot and itself + hull = points.slice(1..-1).sort { |a, b| polar_angle(a, pivot) - polar_angle(b, pivot) } + + # The pivot is always on the hull + hull.unshift(pivot) + + n = hull.length + m = 1 + (2...n).each do |i| + while ccw(hull[m - 1], hull[m], hull[i]) <= 0 + if m > 1 + m -= 1 + elsif m == i + break + else + i += 1 + end + end + + m += 1 + hull[i], hull[m] = [hull[m], hull[i]] + end + + hull.slice(0...m + 1) +end + +def polar_angle(a, b) + Math.atan2(a[:y] - b[:y], a[:x] - b[:x]) +end + +def ccw(a, b, c) + (b[:x] - a[:x]) * (c[:y] - a[:y]) - (c[:x] - a[:x]) * (b[:y] - a[:y]) +end + +points = [ + { x: 1, y: 3 }, + { x: 2, y: 4 }, + { x: 4, y: 0 }, + { x: 1, y: 0 }, + { x: 0, y: 2 }, + { x: 2, y: 2 }, + { x: 3, y: 4 }, + { x: 3, y: 1 } +] + +convex_hull = graham_scan(points) +convex_hull.each { |p| puts("(#{p[:x]}, #{p[:y]})") } diff --git a/contents/graham_scan/graham_scan.md b/contents/graham_scan/graham_scan.md index e0323d961..d53eb1b85 100644 --- a/contents/graham_scan/graham_scan.md +++ b/contents/graham_scan/graham_scan.md @@ -28,6 +28,8 @@ We can find whether a rotation is counter-clockwise with trigonometric functions [import:27-29, lang:"java"](code/java/GrahamScan.java) {% sample lang="cpp" %} [import:10-12, lang="cpp"](code/c++/graham_scan.cpp) +{% sample lang="ruby" %} +[import:36-38, lang:"ruby"](code/ruby/graham_scan.rb) {% endmethod %} If the output of this function is 0, the points are collinear. @@ -58,6 +60,8 @@ In the end, the code should look something like this: [import:35-70, lang:"java"](code/java/GrahamScan.java) {% sample lang="cpp" %} [import:14-47, lang="cpp"](code/c++/graham_scan.cpp) +{% sample lang="ruby" %} +[import:1-30, lang:"ruby"](code/ruby/graham_scan.rb) {% endmethod %} ### Bibliography @@ -83,6 +87,8 @@ In the end, the code should look something like this: [import, lang:"java"](code/java/GrahamScan.java) {% sample lang="cpp" %} [import, lang="cpp"](code/c++/graham_scan.cpp) +{% sample lang="ruby" %} +[import, lang:"ruby"](code/ruby/graham_scan.rb) {% endmethod %}