简体   繁体   中英

With List<KeyValuePair<int, int>>, to make 1 in 4 of the values equal 1

I have a List< KeyValuePair < int,int >>. (I chose this over a dictionary since multiple keys will be the same (though consecutive keys won't be the same); and because I want to output them in the order input).

All the keys are equal to a randNum between 1-5 (inclusive). (The keys don't really matter in relation to my problem).

All the values are the default, 0.

I want to change 1 in 4 of the values to equal 1 (the others still equal 0). The position of the value=1 in each range of 4 is randomly chosen.

An example output of what I want, printing the KVPs in the list:

[5,0]
[3,1]
[2,0]
[5,0]
[1,0]
[4,0]
[2,0]
[3,1]
[1,0]
[5,0]
[4,1]
[2,0]

So, in groups of 4, the values would be: 0100 | 0001 | 0010.

Here's what I've tried to do so far: (the sequence is already set up with all the keys)

List<KeyValuePair<int,int>> sequence = new List<KeyValuePair<int,int>>();

// Populate keys in sequence with random numbers 1-5
// ...

public static void SetSequenceValues()
{
    Random randNum = new Random();
    bool allValuesSet = false;

    // count is the max (non-inclusive) in each range of 4
    // i.e. first loop is 1-4
    // then count += 4
    // second loop is 5-8, etc.
    int count = 5;

    while (allValuesSet == false)
    {
        int randPos = randNum.Next(count - 4, count);

        // If changing the first key in the range,
        // the desired key index = count-4
        if (randPos == count - 4)
        {
            var oldKVP = sequence[count - 4];
            sequence[count - 4] = new KeyValuePair<int, int>(oldKVP.Key, 1);
        }
        else if (randPos == count -3)
        {
            var oldKVP = sequence[count - 3];
            sequence[count -3] = new KeyValuePair<int, int>(oldKVP.Key, 1);
        }
        else if (randPos == count - 2)
        {
            var oldKVP = sequence[count - 2];
            sequence[count - 2] = new KeyValuePair<int, int>(oldKVP.Key, 1);
        }
        else if (randPos == count - 1)
        {
            var oldKVP = sequence[count - 1];
            sequence[count - 1] = new KeyValuePair<int, int>(oldKVP.Key, 1);
        }

        // Increase count by 4, so on the next loop, e.g. the range looked at will be 4-8 (not including 8, i.e. 4-7)
        count += 4;

        if (count >= sequence.Count)
            allValuesSet = true;
    }
}

Currently, the output is kind of close-ish, but, occasionally there'll be a couple of 1s in each group. Also, I think it may be using groups of 5 rather than 4? Though that doesn't seem to entirely be it either.

As far as what's broken with the code you posted I think you have an off by 1 issue somewhere. It looks like you index into the array starting at [1] when the first key should be [0]. Try setting count to 4 initially instead of 5.

...
int count = 4;

while (allValuesSet == false)
{
...

I would recommend trying to use loops and variables in place of hard coded if conditions. You have the right idea but you don't need to check the value of the index, you can just use it in place. So you can collapse the following to make it a lot more readable. You could replace all of

    // If changing the first key in the range,
    // the desired key index = count-4
    if (randPos == count - 4)
    {
        var oldKVP = sequence[count - 4];
        sequence[count - 4] = new KeyValuePair<int, int>(oldKVP.Key, 1);
    }
    else if (randPos == count -3)
    {
        var oldKVP = sequence[count - 3];
        sequence[count -3] = new KeyValuePair<int, int>(oldKVP.Key, 1);
    }
    else if (randPos == count - 2)
    {
        var oldKVP = sequence[count - 2];
        sequence[count - 2] = new KeyValuePair<int, int>(oldKVP.Key, 1);
    }
    else if (randPos == count - 1)
    {
        var oldKVP = sequence[count - 1];
        sequence[count - 1] = new KeyValuePair<int, int>(oldKVP.Key, 1);
    }

With

var oldKVP = sequence[randPos];
sequence[randPos] = new KeyValuePair<int, int>(oldKVP.Key, 1);

To do what you want to do in a more generalized fashion I would do as below

using System;
using System.Collections.Generic;

namespace KvpSequence
{
    class Program
    {
        const int numberOfSequences = 4; // each sequence is a set of 4 ints between 1 and 5
        static void Main(string[] args)
        {
            List<KeyValuePair<int, int>> sequence = new List<KeyValuePair<int, int>>(numberOfSequences * 4);
            Random rand = new Random();

            for (int i = 0; i < numberOfSequences; ++i) {
                var indexToSet = rand.Next(0, 4);
                for (int j = 0; j < 4; ++j)
                    sequence.Add(new KeyValuePair<int, int>(rand.Next(1, 6), j == indexToSet ? 1 : 0));
            }

            foreach (var kvp in sequence)
                Console.WriteLine(kvp);
        }
    }
}

This would be much easier if you used a Dictionary<int, List<int>> . You can just loop through each key and assign a random entry in the collection to 1:

var data = new Dictionary<int, List<int>>();
data.Add(1, new List<int> { 0, 0, 0, 0, 0 });
// continue filling the dictionary...

var random = new Random();
foreach (var entry in data)
{
    entry.Value[random.Next(0, entry.Value.Count - 1)] = 1;
}

// print the results

foreach (var entry in data)
{
    Console.WriteLine("{0} => {1}", entry.Key, entry.Value.Join(", "));
}

If I understand your intent correctly, here is what you might do

public class BucketsWithRandomValues
{
    private Random random = new Random();

    public void FillWithValuesInEachBucket(IList<KeyValuePair<int, int>> list, int bucketSize = 4, int valueToPut = 1)
    {
        if (list == null) throw new ArgumentNullException(nameof(list));

        int i = 0;
        while (i + bucketSize <= list.Count)
        {
            var index = random.Next(i, i + bucketSize); // exclusive upper bound
            list[index] = new KeyValuePair<int, int>(list[index].Key, valueToPut);
            i += bucketSize;
        }
    }
}

I would also suggest using a ValueTuple instead of KeyValuePair since you don't really have the Key-Value semantics because of possible duplicate keys. When people see KeyValuePair they immediately think Dictionary, not List.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM