Làm thế nào có thể tìm thấy một trung bình từ một mảng?
Nếu tôi có mảng:
[0,4,8,2,5,0,2,6]
Tính trung bình sẽ cho tôi 3,375.
Làm thế nào có thể tìm thấy một trung bình từ một mảng?
Nếu tôi có mảng:
[0,4,8,2,5,0,2,6]
Tính trung bình sẽ cho tôi 3,375.
Câu trả lời:
Thử cái này:
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
Lưu ý .to_f
, điều mà bạn sẽ muốn tránh bất kỳ vấn đề nào từ phép chia số nguyên. Bạn cũng có thể làm:
arr = [5, 6, 7, 8]
arr.inject(0.0) { |sum, el| sum + el } / arr.size
=> 6.5
Bạn có thể định nghĩa nó là một phần của Array
một bình luận viên khác đã đề xuất, nhưng bạn cần tránh phân chia số nguyên nếu không kết quả của bạn sẽ sai. Ngoài ra, điều này thường không áp dụng cho mọi loại yếu tố có thể (rõ ràng, trung bình chỉ có ý nghĩa đối với những thứ có thể được tính trung bình). Nhưng nếu bạn muốn đi theo con đường đó, hãy sử dụng:
class Array
def sum
inject(0.0) { |result, el| result + el }
end
def mean
sum / size
end
end
Nếu bạn chưa từng thấy inject
trước đây, nó không kỳ diệu như nó có thể xuất hiện. Nó lặp lại qua từng phần tử và sau đó áp dụng giá trị tích lũy cho nó. Bộ tích lũy sau đó được trao cho phần tử tiếp theo. Trong trường hợp này, bộ tích lũy của chúng tôi chỉ đơn giản là một số nguyên phản ánh tổng của tất cả các phần tử trước đó.
Chỉnh sửa: Bình luận viên Dave Ray đề xuất một cải tiến tốt đẹp.
Chỉnh sửa: Bình luận, đề xuất của Glenn Jackman arr.inject(:+).to_f
, cũng rất hay nhưng có lẽ hơi quá thông minh nếu bạn không biết chuyện gì đang xảy ra. Đây :+
là một biểu tượng; khi được truyền vào để tiêm, nó áp dụng phương thức được đặt tên bằng ký hiệu (trong trường hợp này là phép toán cộng) cho mỗi phần tử so với giá trị của bộ tích lũy.
arr.inject(0.0) { |sum,el| sum + el } / arr.size
.
inject
giao diện, được đề cập trong tài liệu. Các to_proc
nhà điều hành là &
.
Array#inject
thì quá mức cần thiết ở đây. Chỉ cần sử dụng #sum
. Ví dụ:arr.sum.to_f / arr.size
a = [0,4,8,2,5,0,2,6]
a.instance_eval { reduce(:+) / size.to_f } #=> 3.375
Một phiên bản không sử dụng instance_eval
sẽ là:
a = [0,4,8,2,5,0,2,6]
a.reduce(:+) / a.size.to_f #=> 3.375
instance_eval
cho phép bạn chạy mã trong khi chỉ định a
một lần, vì vậy nó có thể được nối với các lệnh khác. Tức là random_average = Array.new(10) { rand(10) }.instance_eval { reduce(:+) / size.to_f }
thay vìrandom = Array.new(10) { rand(10) }; random_average = random.reduce(:+) / random.size
self
bên trong khối đó, bạn sẽ gặp vấn đề.) instance_eval
Sẽ nhiều hơn cho siêu lập trình hoặc DSL.
Tôi tin rằng câu trả lời đơn giản nhất là
list.reduce(:+).to_f / list.size
reduce
là một phương pháp của Enumerable
mixin được sử dụng bởi Array
. Và mặc dù tên của nó, tôi đồng ý với @ShuWu ... trừ khi bạn sử dụng Rails thực hiện sum
.
Tôi đã hy vọng cho Math.alusive (giá trị), nhưng không có may mắn như vậy.
values = [0,4,8,2,5,0,2,6]
average = values.sum / values.size.to_f
sum
phương pháp, vì vậy đây dường như là một câu trả lời đúng sau 6 năm, xứng đáng với giải thưởng Nostradamus.
Các phiên bản Ruby> = 2.4 có phương thức # tổng .
Và để lấy điểm trung bình nổi, bạn có thể sử dụng Integer # fdiv
arr = [0,4,8,2,5,0,2,6]
arr.sum.fdiv(arr.size)
# => 3.375
Đối với phiên bản cũ hơn:
arr.reduce(:+).fdiv(arr.size)
# => 3.375
Một số điểm chuẩn của các giải pháp hàng đầu (theo thứ tự hiệu quả nhất):
array = (1..10_000_000).to_a
Benchmark.bm do |bm|
bm.report { array.instance_eval { reduce(:+) / size.to_f } }
bm.report { array.sum.fdiv(array.size) }
bm.report { array.sum / array.size.to_f }
bm.report { array.reduce(:+).to_f / array.size }
bm.report { array.reduce(:+).try(:to_f).try(:/, array.size) }
bm.report { array.inject(0.0) { |sum, el| sum + el }.to_f / array.size }
bm.report { array.reduce([ 0.0, 0 ]) { |(s, c), e| [ s + e, c + 1 ] }.reduce(:/) }
end
user system total real
0.480000 0.000000 0.480000 (0.473920)
0.500000 0.000000 0.500000 (0.502158)
0.500000 0.000000 0.500000 (0.508075)
0.510000 0.000000 0.510000 (0.512600)
0.520000 0.000000 0.520000 (0.516096)
0.760000 0.000000 0.760000 (0.767743)
1.530000 0.000000 1.530000 (1.534404)
array = Array.new(10) { rand(0.5..2.0) }
Benchmark.bm do |bm|
bm.report { 1_000_000.times { array.reduce(:+).to_f / array.size } }
bm.report { 1_000_000.times { array.sum / array.size.to_f } }
bm.report { 1_000_000.times { array.sum.fdiv(array.size) } }
bm.report { 1_000_000.times { array.inject(0.0) { |sum, el| sum + el }.to_f / array.size } }
bm.report { 1_000_000.times { array.instance_eval { reduce(:+) / size.to_f } } }
bm.report { 1_000_000.times { array.reduce(:+).try(:to_f).try(:/, array.size) } }
bm.report { 1_000_000.times { array.reduce([ 0.0, 0 ]) { |(s, c), e| [ s + e, c + 1 ] }.reduce(:/) } }
end
user system total real
0.760000 0.000000 0.760000 (0.760353)
0.870000 0.000000 0.870000 (0.876087)
0.900000 0.000000 0.900000 (0.901102)
0.920000 0.000000 0.920000 (0.920888)
0.950000 0.000000 0.950000 (0.952842)
1.690000 0.000000 1.690000 (1.694117)
1.840000 0.010000 1.850000 (1.845623)
class Array
def sum
inject( nil ) { |sum,x| sum ? sum+x : x }
end
def mean
sum.to_f / size.to_f
end
end
[0,4,8,2,5,0,2,6].mean
nil
0?
Hãy để tôi mang một cái gì đó vào cạnh tranh để giải quyết sự phân chia bằng không vấn đề:
a = [1,2,3,4,5,6,7,8]
a.reduce(:+).try(:to_f).try(:/,a.size) #==> 4.5
a = []
a.reduce(:+).try(:to_f).try(:/,a.size) #==> nil
Tôi phải thừa nhận, tuy nhiên, "thử" là một người trợ giúp Rails. Nhưng bạn có thể dễ dàng giải quyết điều này:
class Object;def try(*options);self&&send(*options);end;end
class Array;def avg;reduce(:+).try(:to_f).try(:/,size);end;end
BTW: Tôi nghĩ đúng là trung bình của một danh sách trống là không. Trung bình của không có gì là không có gì, không phải 0. Vì vậy, đó là hành vi dự kiến. Tuy nhiên, nếu bạn đổi thành:
class Array;def avg;reduce(0.0,:+).try(:/,size);end;end
kết quả cho Mảng trống sẽ không phải là một ngoại lệ như tôi mong đợi mà thay vào đó, nó trả về NaN ... Tôi chưa bao giờ thấy điều đó trước đây trong Ruby. ;-) Có vẻ là một hành vi đặc biệt của lớp Float ...
0.0/0 #==> NaN
0.1/0 #==> Infinity
0.0.class #==> Float
những gì tôi không thích về giải pháp được chấp nhận
arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5
là nó không thực sự hoạt động theo cách hoàn toàn chức năng. chúng ta cần một mảng biến để tính toán Array.size ở cuối.
để giải quyết vấn đề này hoàn toàn theo chức năng, chúng ta cần theo dõi hai giá trị: tổng của tất cả các phần tử và số phần tử.
[5, 6, 7, 8].inject([0.0,0]) do |r,ele|
[ r[0]+ele, r[1]+1 ]
end.inject(:/)
=> 6.5
Santhosh đã cải thiện giải pháp này: thay vì đối số r là một mảng, chúng ta có thể sử dụng hàm hủy để lập tức tách nó thành hai biến
[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele|
[ sum + ele, size + 1 ]
end.inject(:/)
nếu bạn muốn xem nó hoạt động như thế nào, hãy thêm một số đặt:
[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele|
r2 = [ sum + ele, size + 1 ]
puts "adding #{ele} gives #{r2}"
r2
end.inject(:/)
adding 5 gives [5.0, 1]
adding 6 gives [11.0, 2]
adding 7 gives [18.0, 3]
adding 8 gives [26.0, 4]
=> 6.5
Chúng ta cũng có thể sử dụng một cấu trúc thay vì một mảng để chứa tổng và số đếm, nhưng sau đó chúng ta phải khai báo cấu trúc trước:
R=Struct.new(:sum, :count)
[5, 6, 7, 8].inject( R.new(0.0, 0) ) do |r,ele|
r.sum += ele
r.count += 1
r
end.inject(:/)
end.method
được sử dụng trong ruby, cảm ơn vì điều này!
arr.inject([0.0,0]) { |(sum, size), el| [ sum + el, size + 1 ] }.inject(:/)
Đối với giải trí công cộng, một giải pháp khác:
a = 0, 4, 8, 2, 5, 0, 2, 6
a.reduce [ 0.0, 0 ] do |(s, c), e| [ s + e, c + 1 ] end.reduce :/
#=> 3.375
Array#average
.Tôi đã làm điều tương tự khá thường xuyên vì vậy tôi nghĩ thật thận trọng khi chỉ mở rộng Array
lớp học bằng một average
phương pháp đơn giản . Nó không hoạt động cho bất cứ thứ gì ngoài Mảng số như Số nguyên hoặc Số nổi hoặc Số thập phân nhưng nó rất tiện lợi khi bạn sử dụng đúng.
Tôi đang sử dụng Ruby on Rails vì vậy tôi đã đặt nó vào config/initializers/array.rb
nhưng bạn có thể đặt nó ở bất cứ đâu có trong boot, v.v.
config/initializers/array.rb
class Array
# Will only work for an Array of numbers like Integers, Floats or Decimals.
#
# Throws various errors when trying to call it on an Array of other types, like Strings.
# Returns nil for an empty Array.
#
def average
return nil if self.empty?
self.sum / self.size
end
end
a = [0,4,8,2,5,0,2,6]
sum = 0
a.each { |b| sum += b }
average = sum / a.length
a = [0,4,8,2,5,0,2,6]
a.empty? ? nil : a.reduce(:+)/a.size.to_f
=> 3.375
Giải các phép chia cho số 0, phép chia số nguyên và dễ đọc. Có thể dễ dàng sửa đổi nếu bạn chọn có một mảng trống trả về 0.
Tôi cũng thích biến thể này, nhưng nó dài dòng hơn một chút.
a = [0,4,8,2,5,0,2,6]
a.empty? ? nil : [a.reduce(:+), a.size.to_f].reduce(:/)
=> 3.375
arr = [0,4,8,2,5,0,2,6]
average = arr.inject(&:+).to_f / arr.size
# => 3.375
Phương pháp này có thể hữu ích.
def avg(arr)
val = 0.0
arr.each do |n|
val += n
end
len = arr.length
val / len
end
p avg([0,4,8,2,5,0,2,6])
[1,2].tap { |a| @asize = a.size }.inject(:+).to_f/@asize
Ngắn nhưng sử dụng biến thể hiện
a_size = nil; [1,2].tap { |a| a_size = a.size }.inject(:+).to_f/a_size
hơn là tạo một biến thể hiện.
Bạn có thể thử một cái gì đó như sau:
a = [1,2,3,4,5]
# => [1, 2, 3, 4, 5]
(a.sum/a.length).to_f
# => 3.0