Given an array arr[] of integers, find the sum of the averages of all non-empty subsets.
Example:
Input: arr[] = [1, 2, 3]
Output: 14.000000
Explanation: The non-empty subsets of the array are: {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}. Their respective averages are: 1, 2, 3, 1.5, 2, 2.5, 2. Therefore, the required sum is: 1 + 2 + 3 + 1.5 + 2 + 2.5 + 2 = 14.000000.Input: arr[] = [2, 5]
Output: 10.500000
Explanation: The non-empty subsets of the array are: {2}, {5}, {2, 5}. Their respective averages are: 2, 5, 3.5. Therefore, the required sum is: 2 + 5 + 3.5 = 10.500000.
Table of Content
[Naive Approach] Generate All Subsets - Exponential Time
For every element of the array, we have two choices: either include it in the current subset or exclude it. By recursively making these two choices for every element, we generate all possible subsets.
For every non-empty subset, we maintain its sum and number of elements, calculate its average, and add it to the answer.
[Expected Approach] Group Subsets By Size - O(n ^ 2) Time and O(1) Space
We group all subsets according to their sizes. For a subset containing k elements, its average is its sum divided by k.
Instead of constructing all such subsets, we count how many times each array element appears among these subsets using combinations. .
- Find the number of elements n and calculate the total sum S of all array elements.
- For every subset size k from 1 to n, consider all subsets containing exactly k elements.
- Each element appears in exactly C(n - 1, k - 1) subsets of size k.
- Hence, the total sum of elements across these subsets is S × C(n - 1, k - 1).
- Since every subset has k elements, divide this value by k and add it to the answer.
- Return the accumulated sum of averages of all non-empty subsets.
#include <bits/stdc++.h>
using namespace std;
// Returns C(n, r) in O(r) time.
long double nCr(int n, int r)
{
// Since C(n, r) = C(n, n-r)
r = min(r, n - r);
long double res = 1.0;
for (int i = 1; i <= r; i++)
{
res *= (n - r + i);
res /= i;
}
return res;
}
double averageOfAllSubsets(vector<int> &arr)
{
int n = arr.size();
// Compute the sum of all array elements.
long long sum = 0;
for (int x : arr)
sum += x;
long double ans = 0.0;
/*
Consider all subsets of size k.
Every element appears in exactly
C(n - 1, k - 1) subsets of size k.
Therefore, the total contribution of all
elements to subsets of size k is:
sum * C(n - 1, k - 1)
Since every subset contains k elements,
divide by k to obtain the sum of averages.
*/
for (int k = 1; k <= n; k++)
{
long double ways = nCr(n - 1, k - 1);
ans += (sum * ways) / k;
}
return (double)ans;
}
int main()
{
vector<int> arr = {1, 2, 3};
cout << fixed << setprecision(6) << averageOfAllSubsets(arr);
return 0;
}
import java.util.*;
class GFG {
// Returns C(n, r) in O(r) time.
static double nCr(int n, int r)
{
// Since C(n, r) = C(n, n-r)
r = Math.min(r, n - r);
double res = 1.0;
for (int i = 1; i <= r; i++) {
res *= (n - r + i);
res /= i;
}
return res;
}
static double averageOfAllSubsets(int[] arr)
{
int n = arr.length;
// Compute the sum of all array elements.
long sum = 0;
for (int x : arr)
sum += x;
double ans = 0.0;
/*
Consider all subsets of size k.
Every element appears in exactly
C(n - 1, k - 1) subsets of size k.
Therefore, the total contribution of all
elements to subsets of size k is:
sum * C(n - 1, k - 1)
Since every subset contains k elements,
divide by k to obtain the sum of averages.
*/
for (int k = 1; k <= n; k++) {
double ways = nCr(n - 1, k - 1);
ans += (sum * ways) / k;
}
return ans;
}
public static void main(String[] args)
{
int[] arr = { 1, 2, 3 };
System.out.printf("%.6f", averageOfAllSubsets(arr));
}
}
# Returns C(n, r) in O(r) time.
def nCr(n, r):
# Since C(n, r) = C(n, n-r)
r = min(r, n - r)
res = 1.0
for i in range(1, r + 1):
res *= (n - r + i)
res /= i
return res
def averageOfAllSubsets(arr):
n = len(arr)
# Compute the sum of all array elements.
total = sum(arr)
ans = 0.0
"""
Consider all subsets of size k.
Every element appears in exactly
C(n - 1, k - 1) subsets of size k.
Therefore, the total contribution of all
elements to subsets of size k is:
sum * C(n - 1, k - 1)
Since every subset contains k elements,
divide by k to obtain the sum of averages.
"""
for k in range(1, n + 1):
ways = nCr(n - 1, k - 1)
ans += (total * ways) / k
return ans
# Driver Code
if __name__ == "__main__":
arr = [1, 2, 3]
print(f"{averageOfAllSubsets(arr):.6f}")
using System;
class GFG {
// Returns C(n, r) in O(r) time.
static double NCr(int n, int r)
{
// Since C(n, r) = C(n, n-r)
r = Math.Min(r, n - r);
double res = 1.0;
for (int i = 1; i <= r; i++) {
res *= (n - r + i);
res /= i;
}
return res;
}
static double averageOfAllSubsets(int[] arr)
{
int n = arr.Length;
// Compute the sum of all array elements.
long sum = 0;
foreach(int x in arr) sum += x;
double ans = 0.0;
/*
Consider all subsets of size k.
Every element appears in exactly
C(n - 1, k - 1) subsets of size k.
Therefore, the total contribution of all
elements to subsets of size k is:
sum * C(n - 1, k - 1)
Since every subset contains k elements,
divide by k to obtain the sum of averages.
*/
for (int k = 1; k <= n; k++) {
double ways = NCr(n - 1, k - 1);
ans += (sum * ways) / k;
}
return ans;
}
static void Main()
{
int[] arr = { 1, 2, 3 };
Console.WriteLine(
averageOfAllSubsets(arr).ToString("F6"));
}
}
// Returns C(n, r) in O(r) time.
function nCr(n, r)
{
// Since C(n, r) = C(n, n-r)
r = Math.min(r, n - r);
let res = 1.0;
for (let i = 1; i <= r; i++) {
res *= (n - r + i);
res /= i;
}
return res;
}
function averageOfAllSubsets(arr)
{
const n = arr.length;
// Compute the sum of all array elements.
let sum = 0;
for (const x of arr)
sum += x;
let ans = 0.0;
/*
Consider all subsets of size k.
Every element appears in exactly
C(n - 1, k - 1) subsets of size k.
Therefore, the total contribution of all
elements to subsets of size k is:
sum * C(n - 1, k - 1)
Since every subset contains k elements,
divide by k to obtain the sum of averages.
*/
for (let k = 1; k <= n; k++) {
const ways = nCr(n - 1, k - 1);
ans += (sum * ways) / k;
}
return ans;
}
// Driver Code
const arr = [ 1, 2, 3 ];
console.log(averageOfAllSubsets(arr).toFixed(6));
Output
14.000000
[Optimal Approach] Using Binomial Identity - O(n) Time and O(1) Space
In the previous approach, we found that the contribution of subsets of size
kis:
\frac{S \times \binom{n-1}{k-1}}{k} ,where
Sis the sum of all elements. Using the identity
\frac{\binom{n-1}{k-1}}{k} = \frac{\binom{n}{k}}{n} ,we can rewrite the total answer as
\frac{S}{n} \sum_{k=1}^{n} \frac{n!}{k!(n-k)!} By the binomial theorem, this summation is 2n−1, since we exclude the empty subset. Therefore, the entire problem reduces to the simple formula:
\boxed{\frac{S \times (2^n-1)}{n}} .
- Find the number of elements n and calculate the sum S of all elements.
- There are 2^n total subsets, including the empty subset.
- Exclude the empty subset, so the number of non-empty subsets is 2^n - 1.
- Using the combinatorial derivation, the sum of averages is S × (2^n - 1) / n.
- Calculate this expression using floating-point arithmetic.
- Return the resulting value as the required sum of averages.
#include <bits/stdc++.h>
using namespace std;
double averageOfAllSubsets(vector<int> &arr)
{
int n = arr.size();
// Compute the sum of all array elements.
long long sum = 0;
for (int x : arr)
sum += x;
/*
There are (2^n - 1) non-empty subsets.
From the combinatorial derivation,
the sum of averages of all non-empty subsets is:
sum * (2^n - 1) / n
*/
long double ans = (long double)sum * (pow(2.0L, n) - 1) / n;
return (double)ans;
}
int main()
{
vector<int> arr = {1, 2, 3};
cout << fixed << setprecision(6) << averageOfAllSubsets(arr);
return 0;
}
class GFG {
static double averageOfAllSubsets(int[] arr)
{
int n = arr.length;
// Compute the sum of all array elements.
long sum = 0;
for (int x : arr)
sum += x;
/*
There are (2^n - 1) non-empty subsets.
From the combinatorial derivation,
the sum of averages of all non-empty subsets is:
sum * (2^n - 1) / n
*/
double ans = sum * (Math.pow(2.0, n) - 1) / n;
return ans;
}
public static void main(String[] args)
{
int[] arr = { 1, 2, 3 };
System.out.printf("%.6f", averageOfAllSubsets(arr));
}
}
def averageOfAllSubsets(arr):
n = len(arr)
# Compute the sum of all array elements.
total = sum(arr)
"""
There are (2^n - 1) non-empty subsets.
From the combinatorial derivation,
the sum of averages of all non-empty subsets is:
sum * (2^n - 1) / n
"""
ans = total * (2 ** n - 1) / n
return ans
# Driver Code
if __name__ == "__main__":
arr = [1, 2, 3]
print(f"{averageOfAllSubsets(arr):.6f}")
using System;
class GFG {
static double averageOfAllSubsets(int[] arr)
{
int n = arr.Length;
// Compute the sum of all array elements.
long sum = 0;
foreach(int x in arr) sum += x;
/*
There are (2^n - 1) non-empty subsets.
From the combinatorial derivation,
the sum of averages of all non-empty subsets is:
sum * (2^n - 1) / n
*/
double ans = sum * (Math.Pow(2.0, n) - 1) / n;
return ans;
}
static void Main()
{
int[] arr = { 1, 2, 3 };
Console.WriteLine(
averageOfAllSubsets(arr).ToString("F6"));
}
}
function averageOfAllSubsets(arr) {
const n = arr.length;
// Compute the sum of all array elements.
let sum = 0;
for (const x of arr)
sum += x;
/*
There are (2^n - 1) non-empty subsets.
From the combinatorial derivation,
the sum of averages of all non-empty subsets is:
sum * (2^n - 1) / n
*/
const ans = sum * (Math.pow(2, n) - 1) / n;
return ans;
}
// Driver Code
const arr = [1, 2, 3];
console.log(averageOfAllSubsets(arr).toFixed(6));
Output
14.000000