I want to select K elements uniformly from a list of N elements without shuffling a list. So the algorithm should always produce the same result.
E.g.
int N = 100;
List<int> myList = Enumerable.Range(0, N-1).ToList();
int K = 20;
Here i expect the result: 0, 5, 10, 15, 20, ...
E.g. if K == 50 I expect: 0, 2, 4, 6, 8, ...
But I don't know how to solve it if e.g. K = 53? I still want to take uniformly elements from the list, but in this case we can't simply take each second element from the list. Simple approach would be to shuffle elements and take first K elements from the list, order again the list but in that case the result of algorithm would each time produce different result and i need each time the same result.
Any help would be appreciated.
Find what the indices would be if they were all equidistant, and round to the nearest integer.
int N = 100;
List<int> myList = Enumerable.Range(0, N).ToList();
double K = 53;
List<int> solutions=new List<int>();
for (int i=1;i<=K;i++)
{
solutions.Add(myList[(int)Math.Round(i*(N/K)-1)]);
}
Console.WriteLine(solutions.Count);
Console.WriteLine(string.Join(", ", solutions));
See a sample at: DEMO
EDIT: Math.Floor works better than Math.Round as Math.Round gives the wrong solution to N=5, K=3:
Math.Round->1,2,4
Math.Floor->0,2,4
See a sample at: DEMO2
This problem is exactly same as line drawing algorithm: https://en.wikipedia.org/wiki/Line_drawing_algorithm
In your case, you want to draw a line from (0, 0) to (20, 52)
You could still shuffle the list. Please see the follwing example
var n = 100;
var k = 53;
var originalList = GetListWithNElements(n);
var shuffledList = ShuffleList(originalList);
var firstKElements = GetFirstKElements(k, shuffledList);
[...]
List<int> GetListWithNElements(int n)
{
return Enumerable.Range(0, n-1).ToList();
}
List<int> ShuffleList(List<int> originalList)
{
List<int> copy = originalList.ToList();
List<int> result = new List<int>();
Random randomGenerator = new Random(314159);
while(copy.Any())
{
int currentIndex = randomGenerator.Next(copy.Count);
result.Add(copy[currentIndex]);
copy.RemoveAt(currentIndex);
}
return result;
}
List<int> GetFirstKElements(int k, List<int> shuffledList)
{
return shuffledList.Take(k).ToList();
}
Random is initialized with a constant seed and hence will produce the very same sequence of "random" values each time, hence you will take the same elements each time.
Try following :
static void Main(string[] args)
{
const int N = 100;
{
List<int> myList = Enumerable.Range(0, N - 1).ToList();
int seed = 5;
int numberOfItems = 50;
List<int> results = TakeNItems(myList, seed, numberOfItems);
}
}
static List<int> TakeNItems(List<int> data, int seed, int numberOfItems)
{
Random rand = new Random(seed);
return data.Select((x,i) => new { x = x, index = i, rand = rand.Next()})
.OrderBy(x => x.rand)
.Take(numberOfItems)
.OrderBy(x => x.index)
.Select(x => x.x)
.ToList();
}
Related
I am partaking on an Amazon interview and they tasked us with this coding challenge, Given an integer array arr write code that will return the minimal subset of that array where the elements of the subset yield the higher value against the sum of the remaining subset. An example is the array 3,7,5,6,2, the code needs to return either two subsets, the first one being 5,7 which yields a maximal value of 12 against the sum of the remaining subset 3,6,2 which yields a maximal value of 11. The second one being 6,7 which will yield a maximal value of 13 against the remaining subset 3,5,2 which will yield a maximal value of 10. I think my code is failing at the minimal array elements that yield a maximal value.
The code I have tried is below but its only passing only two of the thirteen test cases.
The test page is a demo assessment but serves as a preparation for an actual test that will come later, help me write this code correctly to return the correct output.
public static List<int> minimalHeaviestSetA(List<int> arr)
{
List<int> sol = new List<int>();List<int> sec= new List<int>();
//try finding the two largest numbers in the array and add
int max=0;
foreach(int u in arr){
if(u>max){
max=u;
}
}
//add the maximum element to the list
//initialize the sum
int sum=0;
//remove the maximum element from the list
arr.Remove(max);
foreach(int p in arr){
if((p+max)>sum){
sum=p+max;
sec.Clear();
sec.Add(p);
}
}
sol.Add(sec[0]);
sol.Add(max);
return sol;
}
Some additional instruction from the site include
The intersection of subset A and B is null
The union of A and B returns the original array
The number of elements in subset A is minimal
The sum of subset A elements is greater than subset B elements
/*
* Given an integer array nums of length n and an integer target, find three integers in nums such that the sum is closest to target.
Return the sum of the three integers.
You may assume that each input would have exactly one solution.
Example 1:
Input: nums = [-1,2,1,-4], target = 1
Output: 2
Explanation: The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).
Example 2:
Input: nums = [0,0,0], target = 1
Output: 0
Constraints:
3 <= nums.length <= 1000
-1000 <= nums[i] <= 1000
-104 <= target <= 104
*/
public class Tuples
{
public int sum;
public int[] Array;
public int distance;
}
public class _3SumClosest
{
public int[] input;
public _3SumClosest()
{
input = new int[] { -1, 2, 1, -4 };
}
public int ThreeSumClosest(int[] nums, int target)
{
//Get comnbination of all indexes of Array
double count = Math.Pow(2, nums.Length);
var list = new List<int>();
for (int i = 0; i < nums.Length; i++)
list.Add(i);
List<int[]> combinations = new List<int[]>();
for (int i = 1; i <= count - 1; i++)
{
string str = Convert.ToString(i, 2).PadLeft(nums.Length, '0');
List<int> temp = new List<int>(); ;
for (int j = 0; j < str.Length; j++)
{
if (str[j] == '1')
{
temp.Add(list[j]);
}
}
combinations.Add(temp.ToArray());
}
//filter combinations with 2sum, 3sum or nsum array combination. Dont filter if no requirement
//if you want 3 number combination
var tupleComb = combinations.Where(x => x.Length == 3);
List<Tuples> tupleList = new List<Tuples>();
foreach(var tuple in tupleComb)
{
var sum = nums[tuple[0]] + nums[tuple[1]] + nums[tuple[2]];
Tuples tp = new Tuples();
tp.sum = sum;
tp.Array = new int[] { nums[tuple[0]], nums[tuple[1]], nums[tuple[2]] };
tupleList.Add(tp);
}
var final = tupleList.Select(x => new Tuples() { sum = x.sum, Array = x.Array, distance = x.sum - target });
return final.OrderBy(x => x.distance).FirstOrDefault().sum;
}
}
Keep removing maximal elements from the list till the sum of elements in subset A return a value greater than the sum of the elements remaining in the original subset using a while loop, I used several test cases for testing the effectiveness of the code. There are functions to return the highest element and others to return the sum of elements in that list.
Console.WriteLine("Test Case 1");
int[] arr = new int[] { 9, 8, 7, 6 ,5,3};
List<int> soln = MinimalElements(arr.ToList());
if (soln != null)
{
foreach (var VARIABLE in soln)
{
Console.WriteLine(VARIABLE);
}
}
Console.WriteLine("Test Case 2");
int[] arr1 = new int[] { 3,5,7,0,11};
List<int> soln1 = MinimalElements(arr1.ToList());
if (soln != null)
{
foreach (var VARIABLE in soln1)
{
Console.WriteLine(VARIABLE);
}
}
Console.WriteLine("Test Case 3");
int[] arr2 = new int[]{0,9};
List<int> soln2 = MinimalElements(arr2.ToList());
if (soln2 != null)
{
foreach (var VARIABLE in soln2)
{
Console.WriteLine(VARIABLE);
}
}
Console.WriteLine("Test Case 4");
int[] arr3 = new int[] { 0, 17, 6, 10, 2 };
List<int> soln3 = MinimalElements(arr3.ToList());
if (soln3 != null)
{
foreach (var r in soln3)
{
Console.WriteLine(r);
}
}
//pseudocode
//get the first element of the array and compare with the sum of the
//remaining elements if its less than the sum of the remaining elements then
static List<int> MinimalElements(List<int> arr)
{
//check if the array contains elements
if (arr.Count() > 0)
{
//sort the array first
arr.Sort();
//create the return list
List<int> result = new List<int>();
//get the maximum of the original list and remove it
int max = MaxInTheList(arr);
//add the maximum to our list
result.Add(max);
//pop the element from the original collection
arr.Remove(max);
//loop through each list while comparing sum and keep adding elements until the sum of the
//resulting list is better than the elements removed from the original list
while (sumList(result) < sumList(arr))
{
int mm = MaxInTheList(arr);
//add the next biggest element to the array
result.Add(mm);
//keep reducing elements from arr
arr.Remove(mm);
}
//return a sorted integer array to the calling function
result.Sort();
return result;
}
else
{
//return an empty integer array
return new List<int>();
}
}
//define method to sum up all the elements of a list
static int sumList(List<int> arr)
{
int sum = 0;
foreach (var s in arr)
{
sum += s;
}
return sum;
}
//first get the maximum integer of each list using a method
static int MaxInTheList(List<int> arr)
{
int max = 0;
foreach (int u in arr)
{
if (u > max)
{
max = u;
}
}
return max;
}
I want the random numbers like:
{1,2,6} {8,9,5} {4,3,5}
You can try Linq:
// Easiest, but not thread safe
private static Random s_Generator = new Random()
...
int outerCount = 3; // 3 groups
int innerCount = 3; // each group has 3 items
int[][] randoms = Enumerable
.Range(0, outerCount)
.Select(x => Enumerable
.Range(0, innerCount)
.Select(y => s_Generator.Next(1, 10)) // let randoms be in [1..9] range
.ToArray())
.ToArray();
Test:
string report = string.Join(" ", randoms
.Select(item => "{" + string.Join(",", item) + "}"));
Console.Write(report);
Outcome (may differ from run to run since we output random numbers):
{7,4,5} {1,7,4} {6,5,6}
You can use the Random class along with yield:
static IEnumerable<Tuple<int, int, int>> GenerateRandomNumbers(int maxValue)
{
var random = new Random();
while(true)
{
var x = random.Next(maxValue);
var y = random.Next(maxValue);
var z = random.Next(maxValue);
yield return Tuple.Create(x, y, z);
}
}
So, if you want 20 triplets between 0 and 10 as an array you can write
var items = GenerateRandomNumbers(10).Take(20).ToArray();
One way to generate random numbers in C# is to use the Random object and to specify the min and max numbers such as:
Random r = new Random();
var n = r.Next(1, 6);//this will generate number between 1 and 5
Console.WriteLine(n);
Edit 1 : to get 3 random numbers at a time, the simplest way I can think of is to just generate the random numbers, and add them to an array:
public int[] generateRandomArray(int arraySize, int min, int max)
{
Random r = new Random();
int[] results = new int[arraySize];
for(int i = 0 ; i < arraySize; i++)
{
var n = r.Next(min, max);
results[i] = n;
}
return results;
}
I have created a short program that creates 3 random integers between 1-9 and stores them in an array, however, I would not like any of them to repeat, that is, I would like each to be unique. Is there an easier way to generate 3 unique integers other than having to iterate through the array and comparing each integer to each other? That just seems so tedious if I were to increase my array to beyond 3 integers.
This is my code to generate 3 random numbers. I saw other code in Java, but I thought maybe C# has a easier and more efficient way to do it.
var number = new Numbers[3];
Random r = new Random();
for ( int i = 0; i < number.Length; i++)
{
number[i] = new Numbers(r.Next(1,9));
}
Console.WriteLine("The Three Random Numbers Are:");
foreach(Numbers num in number)
{
Console.WriteLine("{0}", num.Number);
}
I would do something like this:
var range = Enumerable.Range(1, 8);
var rnd = new Random();
var listInts = range.OrderBy(i => rnd.Next()).Take(3).ToList();
You could make an array or a list of the numbers that might be generated, e.g. 0, 1, 2, 3. Then you generate a number from 0 to this list's length, e.g. 2 and pick list[2] so for the next time you only have 0, 1, 3 in your list.
It takes longer to generate it, especially for long lists but it doesn't repeat numbers.
using System;
using System.Collections.Generic;
public class Test
{
static Random random = new Random();
public static List<int> GenerateRandom(int count)
{
// generate count random values.
HashSet<int> candidates = new HashSet<int>();
// top will overflow to Int32.MinValue at the end of the loop
for (Int32 top = Int32.MaxValue - count + 1; top > 0; top++)
{
// May strike a duplicate.
if (!candidates.Add(random.Next(top))) {
candidates.Add(top);
}
}
// load them in to a list.
List<int> result = new List<int>();
result.AddRange(candidates);
// shuffle the results:
int i = result.Count;
while (i > 1)
{
i--;
int k = random.Next(i + 1);
int value = result[k];
result[k] = result[i];
result[i] = value;
}
return result;
}
public static void Main()
{
List<int> vals = GenerateRandom(10);
Console.WriteLine("Result: " + vals.Count);
vals.ForEach(Console.WriteLine);
}
}
Grate explanation and answers from here
Source http://ideone.com/Zjpzdh
I am using C# and i am trying to find the the average of 5 values but i can only use 2 variables.
How can you input 5 integers into one variable and display the average of said integers
You can use List like this:
var list = new List<int>(){ 1, 2, 3, 4, 5 };
var average = list.Average();
using Average you'll get average of all values in list
Here you have all functions of Enumerable, you can for e.g. sum all values with Sum
Use a collection like a List<int> and the extension method Enumerable.Average:
List<int> numbers = new List<int>{ 10, 20, 30, 40, 50 };
double average = numbers.Average(); // 30.0
Use List.Add to add single integers:
numbers.Add(1);
numbers.Add(2);
numbers.Add(3);
// ...
Take the input values in a Integer list or array then use the following code
List<int> intlist=new List<int>();
intlist.Add(2);
intlist.Add(3);
..
..
var average= intlist.Average();
Using Average will computes the average of a sequence of all the integers in the list.
UPDATE: or if the case is to use integers only then you need to use the following code (Remember to validate the readline() entries)
public decimal Average()
{
int value = 0;
for(int i=0;i<5;i++)
{
value+=ConvertToInt32(Console.ReadLine());
}
return value/5;
}
What about using array? I think array is one variable in your case
int[] input = new int[5];
input[0] = 5;
input[1] = 40;
input[2] = 15;
input[3] = 50;
input[4] = 25;
int sum = 0;
foreach(int i in input)
{
sum = sum + i;
}
sum = sum / input.Length;
Console.WriteLine(sum.ToString());
#up Yeah that's better way!
You dont need arrays, or lists or anything remotely similar. Pseudo-code:
private int sum = 0;
private int count = 0;
while (user inputs valid number)
{
sum += userInput;
count++;
}
return sum / count;
Only two variables.
If you just want solution without List<int> then here it is
int[] arr=new int[5];
arr[0]=10;arr[1]=20;...arr[4]=50;
int sum=0;
foreach(int x in arr)
{
s+=x;
}
s=s/arr.Length;//s is average
If you want list
List<int> list = new List<int>(){ 1, 2, 3, 4, 5 };
var average = list.Average();
I'm trying to do the following.
Let's say I have a List, and I want to generate a new int in a specific range, but the value cannot be already defined in the List.
List<int> PredefinedIntsList = new List<int>() { 1, 3, 4, 8, 9 };
Random rnd = new Random();
int NewRandomValue = rnd.Next(0, 10);
rnd.Next (obviously) comes up with 1, 3, 4, 8 or 9. But I ONLY want it to return 2, 5, 6, 7 or 10.
Any ideas?
As always, LINQ is your friend:
[TestMethod]
public void RandomTest()
{
var except = new[] {1, 2, 3, 5};
GetRandomExcept(1, 5, except).Should().Be(4);
}
private static int GetRandomExcept(int minValue, int maxValue, IEnumerable<int> except)
{
return GetRandoms(minValue, maxValue).Except(except).First();
}
private static IEnumerable<int> GetRandoms(int minValue, int maxValue)
{
var random = new Random();
while (true) yield return random.Next(minValue, maxValue);
}
Just keep in mind that you never should call GetRandoms().ToArray() or .Max() or .OrderBy() and so on, because you will get an endless loop and generate randoms forever.
What you can do though, is call GetRandoms().Take(10).ToArray() to get the next 10 random integers in an array.
Try examining if you can use the contains() method of List class... Simple solution would be to just generate values and checking contains and rejecting values that are already in the List until you get a value that is not. Also it might be more appropriate to use the Set Class because a set can not contain two elements that are equal.
What you need to do sample from other set. Lets say P is your predefinedIntsList and A is {1,2...9}.
You need to create a list, N = A-P. You will randomly sample from this set of numbers. It can be written more elegantly I suppose but see below example.
class Program
{
static void Main(string[] args)
{
List<int> predefinedIntsList = new List<int>() { 1, 3, 4, 8, 9 };
Random rnd = new Random();
List<int> newIntsList = new List<int>();
int upperBound = 10;
for (int i = 0; i < upperBound; i++)
{
if (!predefinedIntsList.Contains(i))
{
newIntsList.Add(i);
}
}
for (int i = 0; i < 20; i++)
{
int newRandomValueIndex = rnd.Next(0, newIntsList.Count);
int newRandomValue = newIntsList[newRandomValueIndex];
Console.WriteLine(newRandomValue);
}
}
}
Output
2
0
6
7
5
5
5
7
0
7
6
6
5
5
5
0
6
7
0
7
rnd.Next (obviously) comes up with 1, 3, 4, 8 or 9. But I ONLY want it
to return 2, 5, 6, 7 or 10.
Obviously not, it will return value that is either 0, 1, 2, 3, 4, 5, 6, 7, 8 or 9 :P. If you're looking to include 10, and not 0, you want .Next(1, 11)
There are two choices that can work: either try generating values until you succeed, or if the range is small enough, generate the range, mark elemens that can't be picked, and sort them to last ones, and then pick random between [0..possibleToPickElementsCount]
The first approach would look something like this:
public static class RandomExtensions
{
public static int Next(this Random random,
int minInclusive,
int maxExclusive,
IList<int> values)
{
// this will crash if values contains
// duplicate values.
var dic = values.ToDictionary(val => val);
// this can go into forever loop,
// think about this a bit.
for(;;){
var randomNumber= random.Next(minInclusive, maxExclusive);
if(!dic.ContainsKey(randomNumber))
return randomNumber;
}
}
}
The second approach is this, however it's only to give you an idea:
public static class RandomExtensions
{
class NormalizedPair
{
public int Value {get;set;}
public PairStatus Status {get;set;}
public NormalizedPair(int value){
Value = value;
}
public enum PairStatus {
Free,
NotFree
}
}
private static Random _internalRandom = new Random();
public static int Next(this Random random,
int minInclusive,
int maxExclusive,
IList<int> values)
{
var elements = maxExclusive - minInclusive;
var normalizedArr = new NormalizedPair[elements];
var normalizedMinInclusive = 0;
var normalizedMaxExclusive = maxExclusive - minInclusive;
var normalizedValues = values
.Select(x => x - minInclusive)
.ToList();
for(var j = 0; j < elements; j++)
{
normalizedArr[j] = new NormalizedPair(j){
Status = NormalizedPair.PairStatus.Free
};
}
foreach(var val in normalizedValues)
normalizedArr[val].Status = NormalizedPair.PairStatus.NotFree;
return normalizedArr
.Where(y => y.Status == NormalizedPair.PairStatus.Free) // take only free elements
.OrderBy(y => _internalRandom.Next()) // shuffle the free elements
.Select(y => y.Value + minInclusive) // project correct values
.First(); // pick first.
}
}
Or, if you're fan of sets:
public static int Next2(this Random random,
int minInclusive,
int maxExclusive,
IList<int> values)
{
var rangeSet = new HashSet<int>(
Enumerable.Range(
minInclusive,
maxExclusive - minInclusive));
// remove gibberish
rangeSet.ExceptWith(new HashSet<int>(values));
// this can be swapped out with
// yates shuffle algorithm
return rangeSet.OrderBy(x => _internalRandom.Next())
.First();
}
Rather than write logic to determine whether the random number has already been selected I prefer to generate a second list with the items in a random order.
That is easy to do with LINQ
var originalList = new List<int>() { 1, 3, 4, 8, 9 };
Random rnd = new Random();
var secondList = originalList.OrderBy(x=>rnd.NextDouble());
The call to rnd.NextDouble returns a random seed that is used by the OrderBy method to sort each int value.
When I need to run thought the randomized items more than once, with a new sort order each time I walk the list, I will use a Queue to store the items. Then dequeue the items as needed. When the queue is empty it's time to refill.
private Queue<int> _randomizedItems;
private void RandomTest()
{
var originalList = new List<int>() { 1, 3, 4, 8, 9 };
Random rnd = new Random();
var temp = originalList.OrderBy(r=>rnd.NextDouble());
_randomizedItems = new Queue<int>(temp);
while (_randomizedItems.Count >0)
{
MessageBox.Show(_randomizedItems.Dequeue().ToString());
}
}