Minimum # of ops to even out an array by....

In summary: Assert.AreEqual(3, Equal.OperationsToEven(chocstart)); } [TestMethod] public void OneManyFour() { int[] chocstart = { 1, 4 }; Assert.AreEqual(4, Equal.OperationsToEven(
  • #1
SlurrerOfSpeech
141
11
I'm trying to solve https://www.hackerrank.com/challenges/equal which is how to make an array's N elements equal through operations restricted to (1) adding 1 to N-1 elements, (2) adding 2 to N-1 elements or (3) Adding 5 to N-1 elements.

I started out by writing some test cases that might be weak:

Code:
        [TestMethod]
        public void EmptyInput()
        {
            int[] chocstart = { };
            Assert.AreEqual(0, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void SingleElement()
        {
            int[] chocstart = { 1 };
            Assert.AreEqual(0, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void TwoOfSameElement()
        {
            int[] chocstart = { 1, 1 };
            Assert.AreEqual(0, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void ManyOfSameElement()
        {
            int[] chocstart = Enumerable.Repeat(2384, 50).ToArray();
            Assert.AreEqual(0, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void OneTwo()
        {
            int[] chocstart = { 1, 2 };
            Assert.AreEqual(1, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void OneThree()
        {
            int[] chocstart = { 1, 3 };
            Assert.AreEqual(1, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void OneSix()
        {
            int[] chocstart = { 1, 6 };
            Assert.AreEqual(1, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void OneOneTwo()
        {
            int[] chocstart = { 1, 1, 2 };
            Assert.AreEqual(1, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void TwoManyOnes()
        {
            int[] chocstart = new int[] { 2 }.Concat(Enumerable.Repeat(1, 50)).ToArray();
            Assert.AreEqual(1, Equal.OperationsToEven(chocstart));
        }
   
        [TestMethod]
        public void OneEleven()
        {
            int[] chocstart = { 1, 11 };
            Assert.AreEqual(2, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void OneFive()
        {
            int[] chocstart = { 1, 5 };
            Assert.AreEqual(2, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void OneManyFives()
        {
            int[] chocstart = new int[] { 1 }.Concat(Enumerable.Repeat(5, 12)).ToArray();
            Assert.AreEqual(2, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void OneSixteen()
        {
            int[] chocstart = { 1, 16 };
            Assert.AreEqual(3, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void OneSeven()
        {
            int[] chocstart = { 1, 7 };
            Assert.AreEqual(2, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void FiveAscending()
        {
            int[] chocstart = { 1, 2, 3, 4, 5 };
            Assert.AreEqual(6, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void SampleInput()
        {
            int[] chocstart = { 2, 2, 3, 7 };
            Assert.AreEqual(2, Equal.OperationsToEven(chocstart));
        }

Those all pass with the algorithm I wrote.

Then I added some large test cases from the problem:

Code:
        [TestMethod]
        public void TestCase_0_0()
        {
            int[] chocstart = { 53, 361, 188, 665, 786, 898, 447, 562, 272, 123, 229, 629, 670, 848, 994, 54, 822, 46, 208, 17, 449, 302, 466, 832, 931, 778, 156, 39, 31, 777, 749, 436, 138, 289, 453, 276, 539, 901, 839, 811, 24, 420, 440, 46, 269, 786, 101, 443, 832, 661, 460, 281, 964, 278, 465, 247, 408, 622, 638, 440, 751, 739, 876, 889, 380, 330, 517, 919, 583, 356, 83, 959, 129, 875, 5, 750, 662, 106, 193, 494, 120, 653, 128, 84, 283, 593, 683, 44, 567, 321, 484, 318, 412, 712, 559, 792, 394, 77, 711, 977, 785, 146, 936, 914, 22, 942, 664, 36, 400, 857 };
            Assert.AreEqual(10605, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void TestCase_0_1()
        {
            int[] chocstart = { 520, 862, 10, 956, 498, 956, 991, 542, 523, 664, 378, 194, 76, 90, 753, 868, 837, 830, 932, 814, 616, 78, 103, 882, 452, 397, 899, 488, 149, 108, 723, 22, 323, 733, 330, 821, 41, 322, 715, 917, 986, 93, 111, 63, 535, 864, 931, 372, 47, 215, 539, 15, 294, 642, 897, 98, 391, 796, 939, 540, 257, 662, 562, 580, 747, 893, 401, 789, 215, 468, 58, 553, 561, 169, 616, 448, 385, 900, 173, 432, 115, 712 };
            Assert.AreEqual(8198, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void TestCase_0_2()
        {
            int[] chocstart = { 761, 706, 697, 212, 97, 845, 151, 637, 102, 165, 200, 34, 912, 445, 435, 53, 12, 255, 111, 565, 816, 632, 534, 617, 18, 786, 790, 802, 253, 502, 602, 15, 208, 651, 227, 305, 848, 730, 294, 303, 895, 846, 337, 159, 291, 125, 565, 655, 380, 28, 221, 549, 13, 107, 166, 31, 245, 308, 185, 498, 810, 139, 865, 370, 790, 444, 27, 639, 174, 321, 294, 421, 168, 631, 933, 811, 756, 498, 467, 137, 878, 40, 686, 891, 499, 204, 274, 744, 512, 460, 242, 674, 599, 108, 396, 742, 552, 423, 733, 79, 96, 27, 852, 264, 658, 785, 76, 415, 635, 895, 904, 514, 935, 942, 757, 434, 498, 32, 178, 10, 844, 772, 36, 795, 880, 432, 537, 785, 855, 270, 864, 951, 649, 716, 568, 308, 854, 996, 75, 489, 891, 331, 355, 178, 273, 113, 612, 771, 497, 142, 133, 341, 914, 521, 488, 147, 953, 26, 284, 160, 648, 500, 463, 298, 568, 31, 958, 422, 379, 385, 264, 622, 716, 619, 800, 341, 732, 764, 464, 581, 258, 949, 922, 173, 470, 411, 672, 423, 789, 956, 583, 789, 808, 46, 439, 376, 430, 749, 151 };
            Assert.AreEqual(18762, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void TestCase_0_3()
        {
            int[] chocstart = { 134, 415, 784, 202, 34, 584, 543, 119, 701, 7, 700, 959, 956, 975, 484, 426, 738, 508, 201, 527, 816, 136, 668, 624, 535, 108, 1, 965, 857, 152, 478, 344, 567, 262, 546, 953, 199, 90, 72, 900, 449, 773, 211, 758, 100, 696, 536, 838, 204, 738, 717, 21, 874, 385, 997, 761, 845, 998, 78, 703, 502, 557, 47, 421, 819, 945, 375, 370, 35, 799, 622, 837, 924, 834, 595, 24, 882, 483, 862, 438, 221, 931, 811, 448, 317, 809, 561, 162, 159, 640, 217, 662, 197, 616, 435, 368, 562, 162, 739, 949, 962, 713, 786, 238, 899, 733, 263, 781, 217, 477, 220, 790, 409, 383, 590, 726, 192, 152, 240, 352, 792, 458, 366, 341, 74, 801, 709, 988, 964, 800, 938, 278, 514, 76, 516, 413, 810, 131, 547, 379, 609, 119, 169, 370, 502, 112, 448, 695, 264, 688, 399, 408, 498, 765, 749, 925, 918, 458, 913, 234, 611 };
            Assert.AreEqual(16931, Equal.OperationsToEven(chocstart));
        }

        [TestMethod]
        public void TestCase_0_4()
        {
            int[] chocstart = { 512, 125, 928, 381, 890, 90, 512, 789, 469, 473, 908, 990, 195, 763, 102, 643, 458, 366, 684, 857, 126, 534, 974, 875, 459, 892, 686, 373, 127, 297, 576, 991, 774, 856, 372, 664, 946, 237, 806, 767, 62, 714, 758, 258, 477, 860, 253, 287, 579, 289, 496 };
            Assert.AreEqual(5104, Equal.OperationsToEven(chocstart));
        }

Those all fail and I they're too big to debug and figure out why.

I've detailed my algorithm below.

Code:
    /// <summary>
    /// Provides a solution to https://www.hackerrank.com/challenges/equal
    /// </summary>
    public static class Equal
    {
        #region LongExplanation
        /// <summary>
        /// Computes the minimum number of operations needed to even out
        /// the elements of the array through operations limited to adding 
        /// either the same number 1, 2 or 5 to every element besides one.
        /// 
        /// The following describes how it works. 
        /// 
        /// chocs = { 5, 1, 11, 6, 1, 4, 1 }
        /// 
        /// Put in ascending order, remove repeats, make into a queue: 
        /// 
        /// Q = { 1, 4, 5, 6, 11 }
        /// 
        /// Dequeue the first two elements, save into variables:
        /// 
        /// last = 1, 
        /// cur = 4, 
        /// Q = { 5, 6, 11 }
        /// 
        /// It takes 2 operations to increment 1 to 4, because we increment
        /// by 2 and then 1. Save this total number of operations into a variable:
        /// 
        /// ops = 2
        /// 
        /// We have to add 4-1=3 to every element after the current one, so save 
        /// that number in a variable too: 
        /// 
        /// extra = 3
        /// 
        /// Move our current and last elements:
        /// 
        /// last = cur = 4
        /// cur = Q.Dequeue() = 5, 
        /// Q = { 6, 11 }
        /// 
        /// Add the extra to the current element: 
        /// 
        /// cur = 5 + 3 = 8
        /// 
        /// It takes 2 operations to increment last=4 to cur=8, because we incremenet
        /// by 2 twice. Update the total number of oprations: 
        /// 
        /// ops = 2 + 2 = 4
        /// 
        /// And add the difference of 4 to extra: 
        /// 
        /// extra = 3 + 4 = 7
        /// 
        /// We repeat this process for two more iterations, until the queue is empty:
        /// 
        /// -------------------------
        /// last = cur = 8
        /// cur = Q.Dequeue() = 6
        /// Q = { 11 }
        /// cur += 7
        /// ops += 1 (add 5 to last=8 to get cur=13)
        /// extra += 5
        /// --------------------------
        /// last = cur = 13
        /// cur = Q.Dequeue() = 11
        /// Q = { }
        /// cur += 12
        /// ops += 2 (add 5 twice to last=13 to get cur=23)
        /// extra += 10
        /// ---------------------------
        /// 
        /// Finally we return the cumulative number of operations, 
        /// 
        /// ops = 7
        /// 
        /// Time-Complexity: O(n*log(n)) where n = chocs.Length
        /// </summary>
        #endregion
        public static int OperationsToEven(int[] chocs)
        {
            Queue<int> q = new Queue<int>(chocs.Distinct().OrderBy(c => c));
            if(q.Count < 2)
            {
                return 0;
            }
            int extra = 0;
            int ops = 0;
            int last = q.Dequeue(), cur;
            do
            {
                cur = q.Dequeue() + extra;
                int diff = cur - last;
                extra += diff;
                ops += StepsToCloseDiff(diff);
                last = cur;
            } while(q.Count > 0);
            return ops;
        }

        /// <summary>
        /// Computes the least number of steps it takes to raise the
        /// number of chocolates by increments of 1, 2 and 5.
        /// 
        /// For example, if we need to raise the number by 14, we do
        /// 
        /// Step 1: Add 5
        /// Step 2: Add 5
        /// Step 3: Add 2
        /// Step 4: Add 2
        /// 
        /// Time-Complexity: O(1)
        /// </summary>
        private static int StepsToCloseDiff(int diff)
        {
            int steps = 0;
            steps += diff / 5;
            diff %= 5;
            steps += diff / 2;
            diff %= 2;
            steps += diff;
            return steps;
        }
    }

Can someone help me figure out where I'm going wrong with these larger test cases?

They are all off by "a little", e.g. Expected=16931 and Actual=16292.
 
Technology news on Phys.org
  • #2
Why don't you test intermediate cases?
[1,5,5] is a particularly interesting test case.

You assume that it is optimal to bring the lowest colleague to the level of the next-lowest. That is not always optimal.

There is a simplification to the problem that saves a lot of trouble with adding numbers. Subtract.
 
  • #3
mfb said:
There is a simplification to the problem that saves a lot of trouble with adding numbers. Subtract.

Huh?
 
  • #4
The absolute number of chocolates for everyone does not matter, only differences are interesting. Giving "everyone but X" N elements is equivalent to taking N away from X. The latter makes it much easier to find the optimal solution.
 
  • #5
SlurrerOfSpeech said:
Can someone help me figure out where I'm going wrong with these larger test cases?

They are all off by "a little", e.g. Expected=16931 and Actual=16292.
I haven't studied the code... at all... but try to remove the call to Distinct() and see what happens.
 
  • #6
Boom.

Code:
        public static int OperationsToEven(int[] chocs)
        {
            if(chocs.Length < 2)
                return 0;
            int min = chocs.Min();
            return chocs.Sum(i => StepsToCloseDiff(i - min));
        }
 
  • #7
Nevermind. This fails for {1, 5, 5}. Expected: 3. Actual: 4.
 
  • #8
See post #2.
Sometimes it is good to reduce all chocolates / pick every colleague at least once (but with different amounts).

[2,5,5,5] is another interesting test case once your code handles [1,5,5] correctly.
 
  • #9
So the solution is

Min(MyWay, OtherWay)

??

How could one prove that?
 
  • #10
SlurrerOfSpeech said:
So the solution is

Min(MyWay, OtherWay)

??

How could one prove that?
What do you mean by OtherWay?

I admit I've looked at the discussion where the solution is described, I don't know if you want to figure it out yourself or not?
 

What is the minimum number of operations required to even out an array?

The minimum number of operations required to even out an array is determined by the number of elements in the array and their values. It also depends on the specific method or algorithm being used to even out the array.

What is the definition of "evening out" an array?

Evening out an array refers to the process of rearranging the elements in the array in a way that results in a more balanced distribution of values. This can involve shifting, swapping, or performing mathematical operations on the elements.

What are some common methods for evening out an array?

Some common methods for evening out an array include sorting the elements, using mathematical operations to adjust the values, or using a specific algorithm designed for this purpose.

Why is it important to even out an array?

Evening out an array can be important in situations where the distribution of values in the array affects the overall performance or accuracy of a program or calculation. It can also make the data more visually appealing and easier to interpret.

Can the minimum number of operations to even out an array vary depending on the array's elements?

Yes, the minimum number of operations required to even out an array can vary depending on the elements in the array. Some values may require more operations to be evenly distributed compared to others. The size of the array can also impact the minimum number of operations needed.

Similar threads

  • Programming and Computer Science
Replies
5
Views
1K
  • Programming and Computer Science
Replies
23
Views
1K
  • Programming and Computer Science
Replies
13
Views
4K
  • Programming and Computer Science
Replies
1
Views
1K
  • Programming and Computer Science
3
Replies
75
Views
4K
  • Programming and Computer Science
Replies
2
Views
5K
  • Programming and Computer Science
2
Replies
36
Views
3K
  • Programming and Computer Science
Replies
3
Views
2K
  • Programming and Computer Science
7
Replies
235
Views
10K
  • Engineering and Comp Sci Homework Help
Replies
18
Views
1K
Back
Top