Beispiel #1
0
        public void TestPartition()
        {
            var fixtures = new [] {
                Tuple.Create(new int[] { }, 1),
                Tuple.Create((int[])null, 1),

                Tuple.Create(new[] { 5, 5, 5, 5, 5, 5, 5 }, 5),
                Tuple.Create(new[] { 3, 5, 8, 5, 10, 2, 1 }, 5),
                Tuple.Create(new[] { 7, 5, 8, 5, 10, 6, 11 }, 5),
                Tuple.Create(new[] { 5, 5, 10, 15, 7, 3, 5, 5, 2, 1, 9, 9, 0, 3, 5 }, 5),
            };

            var methods = new PartitioningMethod <int>[] { PartitionReorderNode, PatitionReorderValues };

            foreach (var method in methods)
            {
                foreach (var fixture in fixtures)
                {
                    var pivot  = fixture.Item2;
                    var result = method(CreateListFromArray(fixture.Item1), pivot);

                    var greaterFound = false;
                    for (var node = result; node != null; node = node.Next)
                    {
                        if (node.Value.CompareTo(pivot) < 0)
                        {
                            if (greaterFound)
                            {
                                Assert.Fail();
                            }
                        }
                        else
                        {
                            greaterFound = true;
                        }
                    }
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// 분할 방법에 따라 총액(<paramref name="totalAmount"/>)을 분할 갯수(<paramref name="partitionCount"/>)만큼 분할합니다.
        /// </summary>
        /// <param name="method"></param>
        /// <param name="partitionCount"></param>
        /// <param name="totalAmount"></param>
        /// <returns></returns>
        public static double[] Partitioning(this PartitioningMethod method, int partitionCount = DefaultPartitionCount,
                                            double totalAmount = DefaultPartitionAmount)
        {
            partitionCount.ShouldBePositive("partitionCouunt");
            totalAmount.ShouldBePositive("totalAmount");

            if (IsDebugEnabled)
            {
                log.Debug("총액을 분배합니다. PartitioningMethod=[{0}], partitionCount=[{1}], totalAmount=[{2}]", method, partitionCount,
                          totalAmount);
            }

            if (partitionCount == DefaultPartitionCount && Math.Abs(totalAmount - DefaultPartitionAmount) < double.Epsilon)
            {
                return(DefaultPartitioningArray[method]);
            }

            Guard.Assert(() => partitionCount % 2 == 0, "PartitionCount는 짝수여야 합니다. PartitionCount=[{0}]", partitionCount);

            double[] result;
            double   a, b;

            var half            = partitionCount / 2;
            var uniformParition = totalAmount / partitionCount;

            switch (method)
            {
            case PartitioningMethod.HorizontaolUniform:
                result = Generate(partitionCount, x => uniformParition).ToArray <double>();
                break;

            case PartitioningMethod.VerticalBegin:
                result    = new double[partitionCount];
                result[0] = totalAmount;
                break;

            case PartitioningMethod.VerticalEnd:
                result = new double[partitionCount];
                result[partitionCount - 1] = totalAmount;
                break;

            case PartitioningMethod.TriangleBegin:
                a      = -16.0 / 9.0;
                b      = 18.0 * totalAmount / DefaultPartitionAmount;
                result = Generate <double>(partitionCount, x => a * x + b).Select(y => Math.Round(y, 0)).ToArray <double>();
                break;

            case PartitioningMethod.TriangleMiddle:
                result = new double[partitionCount];
                b      = 2.0 * totalAmount / DefaultPartitionAmount;
                var triangleMiddle = Generate <double>(half, x => 4 * x + b).Select(y => Math.Round(y, 0)).ToArray <double>();

                Array.Copy(triangleMiddle, result, half);
                Array.Copy(triangleMiddle.Reverse().ToArray <double>(), 0, result, half, half);

                break;

            case PartitioningMethod.TriangleEnd:
                a      = 16.0 / 9.0;
                b      = 2.0 * totalAmount / DefaultPartitionAmount;
                result = Generate <double>(partitionCount, x => a * x + b).Select(y => Math.Round(y, 0)).ToArray <double>();
                break;

            case PartitioningMethod.TwoStepBegin:

                Func <int, double> @twostepBegin = x => (x < half) ? 1.3 * uniformParition : 0.7 * uniformParition;
                result = Generate <double>(partitionCount, x => @twostepBegin(x)).Select(y => Math.Round(y, 0)).ToArray <double>();
                break;

            case PartitioningMethod.TwoStepEnd:
                Func <int, double> @twostepEnd = x => (x < half) ? 0.7 * uniformParition : 1.3 * uniformParition;
                result = Generate <double>(partitionCount, x => @twostepEnd(x)).Select(y => Math.Round(y, 0)).ToArray <double>();
                break;

            case PartitioningMethod.TrapezoidMiddle:
                result = new double[partitionCount];
                b      = 2.0 * totalAmount / DefaultPartitionAmount;
                var trapezoid = Generate <double>(half - 1, x => 13.0 / 3.0 * x + b).Select(y => Math.Round(y, 0)).ToArray <double>();

                Array.Copy(trapezoid, result, half - 1);
                Array.Copy(trapezoid.Reverse().ToArray(), 0, result, half + 1, half - 1);
                result[half] = result[half - 1];
                break;

            case PartitioningMethod.Normal:

                result =
                    Generate <double>(partitionCount, x => totalAmount * NormalDistribution(4.0, half, x + 1)).Select(
                        y => Math.Round(y, 0)).ToArray <double>();
                break;

            case PartitioningMethod.ThreeMiddle:

                var countByThree = 0.3 * partitionCount;
                var countBySeven = 0.7 * partitionCount;
                Func <int, double> @treeMiddle =
                    x => (x <= countByThree || x >= countBySeven) ? 0.8 * uniformParition : 1.3 * uniformParition;

                result = Generate <double>(partitionCount, x => @treeMiddle(x)).Select(y => Math.Round(y, 0)).ToArray <double>();
                break;

            default:
                throw new NotSupportedException(string.Format("지원하지 않는 분할 방식입니다. method=[{0}]", method));
            }

            return(result);
        }
        public static IEnumerable <IEnumerable <T> > SequentialPartition <T>(this IEnumerable <T> source, Func <T, bool> partitioningPredicate, PartitioningMethod partitioningMethod = PartitioningMethod.Ignore)
        {
            T?  previousBoundary    = default;
            var previousBoundarySet = false;

            while (source.Any())
            {
                var partitionIterator = source.TakeWhile(i => !partitioningPredicate(i));
                source = source.Skip(partitionIterator.Count());
                if (previousBoundarySet)
                {
                    partitionIterator   = partitionIterator.Prepend(previousBoundary !);
                    previousBoundarySet = false;
                }
                var partition = partitionIterator.ToList().AsReadOnly();

                if (!source.TryGetFirst(out var boundary))
                {
                    yield return(partition.Any() ? partition : throw new InvalidOperationException("Invalid partitioning state."));

                    break;
                }

                source = source.Skip(1);

                if (!partitioningPredicate(boundary))
                {
                    throw new InvalidOperationException("Invalid partitioning state.");
                }

                switch (partitioningMethod)
                {
                case PartitioningMethod.Ignore:
                    if (partition.Count > 0)
                    {
                        yield return(partition);
                    }
                    break;

                case PartitioningMethod.KeepWithLast:
                    yield return(partition.Append(boundary));

                    break;

                case PartitioningMethod.KeepSingle:
                    if (partition.Count > 0)
                    {
                        yield return(partition);
                    }
                    yield return(new[] { boundary }.ToList().AsReadOnly());

                    break;

                case PartitioningMethod.KeepWithNext:
                    if (partition.Count > 0)
                    {
                        yield return(partition);
                    }
                    previousBoundary    = boundary;
                    previousBoundarySet = true;
                    break;

                default:
                    throw new InvalidOperationException();
                }
            }
        }