Ruby Program to Implement Bucket Sort

Ruby Program to Implement Bucket Sort

Sorting is an essential part of programming that helps organize data so it can be easily analyzed and processed. One of the interesting and efficient sorting algorithms is Bucket Sort. Unlike algorithms that compare every element to one another, Bucket Sort distributes elements into a number of “buckets” based on a specific range or criteria, then sorts each bucket individually and merges them to produce the final sorted array. This approach is particularly useful when the input is uniformly distributed over a known range.

Pluralsight Logo
Accelerate your tech career
with hands-on learning.
Whether you're a tech newbie or a total pro,
get the skills and confidence to land your next move.
Start 10-Day Free Trial

Bucket Sort is widely used in situations where numbers or data points are evenly spread, such as floating-point numbers between 0 and 1, grades, or measurements. Learning Bucket Sort in Ruby introduces beginners to array manipulation, loops, and practical ways of dividing a problem into smaller, manageable parts. In this article, we will explore multiple implementations of Bucket Sort, including ascending and descending order, recursion, and sorting numeric and string data. By the end, beginners will have a clear understanding of how Bucket Sort works and how to apply it in Ruby.

Program 1: Bucket Sort Using Loops

This first program demonstrates Bucket Sort using loops. It sorts a predefined array of floating-point numbers in ascending order.

# Program 1: Bucket Sort using loops
def bucket_sort(array)

  n = array.length
  return array if n <= 1

  # Create empty buckets
  buckets = Array.new(n) { [] }

  # Put array elements into buckets
  array.each do |num|

    index = (num * n).floor
    buckets[index] << num

  end

  # Sort individual buckets
  buckets.each { |bucket| bucket.sort! }

  # Concatenate buckets
  sorted_array = buckets.flatten
  sorted_array

end

numbers = [0.42, 0.32, 0.33, 0.52, 0.37, 0.47, 0.51]

sorted_array = bucket_sort(numbers)

puts "Sorted array: #{sorted_array}"

In this program, each element is placed into a bucket based on its value relative to the maximum possible value. Each bucket is then sorted individually using Ruby’s built-in sort! method. Finally, all buckets are combined to form a fully sorted array. Beginners can see how dividing the array into smaller parts makes the sorting process easier and more efficient.

Program 2: Bucket Sort in Descending Order

Bucket Sort can be modified to sort data in descending order by sorting each bucket in reverse.

# Program 2: Bucket Sort in descending order
def bucket_sort_desc(array)

  n = array.length

  return array if n <= 1

  buckets = Array.new(n) { [] }

  array.each do |num|

    # assume values are in [0, 1]. If num == 1.0, clamp index to n-1

    index = (num * n).floor
    index = n - 1 if index == n

    buckets[index] << num

  end

  # sort each bucket descending
  buckets.each { |bucket| bucket.sort! { |a, b| b <=> a } }

  # flatten from highest-index bucket to lowest so overall order is descending
  sorted_array = buckets.reverse.flatten
  sorted_array

end

numbers = [0.25, 0.36, 0.58, 0.41, 0.29]
sorted_array = bucket_sort_desc(numbers)

puts "Sorted array (descending): #{sorted_array.inspect}"

Here, each bucket is sorted in descending order before concatenating them. This demonstrates to beginners that small adjustments in sorting logic can easily change the overall order without modifying the bucket distribution process.

Program 3: Recursive Bucket Sort

Bucket Sort can also be implemented recursively, where each bucket is sorted using another call to the bucket sort function. This approach introduces beginners to recursion.

# Program 3: Recursive Bucket Sort
def bucket_sort_recursive(array, depth = 0, max_depth = 20)

  n = array.length

  return array if n <= 1

  buckets = Array.new(n) { [] }

  # distribute into n buckets
  array.each do |num|

    index = (num * n).floor
    index = n - 1 if index >= n

    buckets[index] << num

  end

  sorted_array = []

  buckets.each do |bucket|

    next if bucket.empty?

    # if bucket has no variation, just add it
    if bucket.uniq.length == 1

      sorted_array.concat(bucket)

    # small buckets can be directly sorted
    elsif bucket.length <= 1

      sorted_array.concat(bucket)

    # if we reached max recursion depth, sort the bucket directly
    elsif depth >= max_depth
      sorted_array.concat(bucket.sort)
    else

      # otherwise, recurse on this bucket
      sorted_array.concat(bucket_sort_recursive(bucket, depth + 1, max_depth))

    end
  end

  sorted_array

end

numbers = [0.78, 0.17, 0.39, 0.26, 0.72, 0.94, 0.21]
sorted_array = bucket_sort_recursive(numbers)

puts "Sorted array (recursive): #{sorted_array.inspect}"

In this recursive version, each bucket is passed back into the bucket_sort_recursive function until all buckets have one element or none. Beginners can understand recursion better by seeing how the same algorithm applies to smaller subsets of data.

Program 4: Bucket Sort for Integers

Bucket Sort is not limited to floating-point numbers. This example demonstrates sorting a predefined array of integers.

# Program 4: Bucket Sort for integers
def bucket_sort_integers(array)

  max_value = array.max
  n = array.length
  return array if n <= 1

  buckets = Array.new(n) { [] }

  array.each do |num|
    index = ((num.to_f / (max_value + 1)) * n).floor
    buckets[index] << num
  end

  buckets.each { |bucket| bucket.sort! }

  sorted_array = buckets.flatten
  sorted_array

end

numbers = [42, 32, 33, 52, 37, 47, 51]

sorted_array = bucket_sort_integers(numbers)

puts "Sorted integer array: #{sorted_array}"

This example shows that Bucket Sort can handle integer datasets by scaling elements to fit into the buckets. It demonstrates flexibility, and beginners can understand how mapping numbers to buckets helps in efficiently organizing and sorting data.

Program 5: Optimized Bucket Sort

This version improves the standard Bucket Sort by using fewer buckets and sorting them directly in place, which reduces overhead and memory consumption.

# Program 5: Optimized Bucket Sort
def optimized_bucket_sort(array)

  n = array.length
  return array if n <= 1

  min_val = array.min
  max_val = array.max
  range = max_val - min_val + 1

  # Create fewer buckets based on array size
  bucket_count = [n, range].min
  buckets = Array.new(bucket_count) { [] }

  # Distribute elements into buckets
  array.each do |num|

    index = ((num - min_val) * (bucket_count - 1) / range.to_f).floor
    buckets[index] << num

  end

  # Sort each bucket in place and collect results
  sorted_array = []

  buckets.each do |bucket|

    bucket.sort!  # Using Ruby's built-in sort for simplicity
    sorted_array.concat(bucket)

  end

  sorted_array

end

numbers = [42, 32, 33, 52, 37, 47, 51, 29, 60]

sorted_array = optimized_bucket_sort(numbers)

puts "Optimized sorted array: #{sorted_array}"

In this optimized approach, the minimum and maximum values of the array are used to scale elements into fewer buckets. Each bucket is sorted individually using Ruby’s built-in sort method, and the results are concatenated to form the final sorted array. Beginners can see that this method reduces the number of buckets and memory usage while still keeping the logic of distributing and sorting elements clear. It’s particularly useful when dealing with large datasets where creating too many buckets would be inefficient.

This optimized version demonstrates how small adjustments—like scaling and limiting bucket numbers—can significantly improve performance without changing the core logic of Bucket Sort. It’s a great next step for beginners who want to understand practical optimizations in algorithms.

Frequently Asked Questions (FAQ)

Bucket Sort raises a few common questions for beginners:

Q1: What is the time complexity of Bucket Sort?
The average time complexity of Bucket Sort is O(n + k), where n is the number of elements and k is the number of buckets. Worst-case complexity can be O(n²) if elements are unevenly distributed.

Q2: Is Bucket Sort stable?
Yes, Bucket Sort is stable because the relative order of equal elements is preserved during sorting within buckets.

Q3: Can Bucket Sort handle negative numbers?
Yes, but it requires shifting the range so all numbers are positive or adjusting the bucket mapping formula.

Q4: When should I use Bucket Sort?
Bucket Sort is ideal for uniformly distributed data over a known range, such as grades, measurements, or floating-point numbers between 0 and 1. It is less effective for arbitrary or highly skewed datasets.

Conclusion

In this article, we explored how to implement Bucket Sort in Ruby using loops, descending order, recursion, and integer datasets. Bucket Sort is an efficient and flexible algorithm that introduces beginners to dividing a problem into smaller parts, array manipulation, and stable sorting. By practicing these examples, you will gain confidence in sorting algorithms beyond simple comparisons. Bucket Sort is especially useful for datasets that are evenly distributed, and experimenting with it in Ruby will strengthen your understanding of algorithmic thinking and problem-solving.

Scroll to Top