コード例 #1
0
        /// <summary>
        /// Initialises a new instance of the MoreComplexDataStructures.UniqueRandomGenerator class.
        /// </summary>
        /// <param name="rangeStart">The inclusive start of the range to generate random numbers from.</param>
        /// <param name="rangeEnd">The inclusive end of the range to generate random numbers from.</param>
        /// <exception cref="System.ArgumentException">Parameter 'rangeEnd' is less than parameter 'rangeStart'.</exception>
        /// <exception cref="System.ArgumentException">The total inclusive range exceeds Int64.MaxValue.</exception>
        public UniqueRandomGenerator(Int64 rangeStart, Int64 rangeEnd)
        {
            if (rangeEnd < rangeStart)
            {
                throw new ArgumentException($"Parameter '{nameof(rangeEnd)}' must be greater than or equal to parameter '{nameof(rangeStart)}'.", nameof(rangeEnd));
            }
            if (rangeStart < 1)
            {
                if (rangeStart + Int64.MaxValue <= rangeEnd)
                {
                    throw new ArgumentException("The total inclusive range cannot exceed Int64.MaxValue.", nameof(rangeEnd));
                }
            }
            else if (rangeEnd > -2)
            {
                if (rangeEnd - Int64.MaxValue >= rangeStart)
                {
                    throw new ArgumentException("The total inclusive range cannot exceed Int64.MaxValue.", nameof(rangeEnd));
                }
            }
            rangeTree = new RangeTree();
            var baseRange          = new LongIntegerRange(rangeStart, rangeEnd - rangeStart + 1);
            var baseRangeAndCounts = new RangeAndSubtreeCounts(baseRange, 0, 0);

            rangeTree.Add(baseRangeAndCounts);
            this.rangeStart       = rangeStart;
            this.rangeEnd         = rangeEnd;
            numbersGeneratedCount = 0;
            randomGenerator       = new DefaultRandomGenerator();
        }
コード例 #2
0
        /// <summary>
        /// Returns a unique random number from within the range.
        /// </summary>
        /// <returns>The random number.</returns>
        /// <exception cref="System.InvalidOperationException">No further unique numbers remain in the range.</exception>
        public Int64 Generate()
        {
            if (NumbersRemainingCount == 0)
            {
                throw new InvalidOperationException("Cannot generate a random number as no further unique numbers exist in the specified range.");
            }

            WeightBalancedTreeNode <RangeAndSubtreeCounts> currentNode = rangeTree.RootNode;
            Int64 returnNumber = 0;

            while (true)
            {
                // Decide whether to stop at this node, or to move left or right
                Int64 randomRange  = currentNode.Item.LeftSubtreeRangeCount + currentNode.Item.Range.Length + currentNode.Item.RightSubtreeRangeCount;
                Int64 randomNumber = randomGenerator.Next(randomRange);
                if (randomNumber < currentNode.Item.LeftSubtreeRangeCount)
                {
                    // Move to the left child node
                    currentNode.Item.LeftSubtreeRangeCount--;
                    currentNode = currentNode.LeftChildNode;
                }
                else if (randomNumber >= (currentNode.Item.LeftSubtreeRangeCount + currentNode.Item.Range.Length))
                {
                    // Move to the right child node
                    currentNode.Item.RightSubtreeRangeCount--;
                    currentNode = currentNode.RightChildNode;
                }
                else
                {
                    // Stop traversing at the current node
                    returnNumber = (randomNumber - currentNode.Item.LeftSubtreeRangeCount) + currentNode.Item.Range.StartValue;
                    break;
                }
            }
            if (returnNumber == currentNode.Item.Range.StartValue)
            {
                if (currentNode.Item.Range.Length == 1)
                {
                    if (currentNode.LeftChildNode != null && currentNode.RightChildNode != null)
                    {
                        // The current node will be swapped with the next less than or next greater than, so need to update the subtree range counts between the current and swapped nodes
                        WeightBalancedTreeNode <RangeAndSubtreeCounts> swapNode = null;
                        if (currentNode.LeftSubtreeSize > currentNode.RightSubtreeSize)
                        {
                            // The current node will be swapped with the next less than
                            swapNode = rangeTree.GetNextLessThan(currentNode);
                            swapNode.Item.LeftSubtreeRangeCount  = currentNode.Item.LeftSubtreeRangeCount - swapNode.Item.Range.Length;
                            swapNode.Item.RightSubtreeRangeCount = currentNode.Item.RightSubtreeRangeCount;
                        }
                        else
                        {
                            // The current node will be swapped with the next greater than
                            swapNode = rangeTree.GetNextGreaterThan(currentNode);
                            swapNode.Item.LeftSubtreeRangeCount  = currentNode.Item.LeftSubtreeRangeCount;
                            swapNode.Item.RightSubtreeRangeCount = currentNode.Item.RightSubtreeRangeCount - swapNode.Item.Range.Length;
                        }
                        // Update the subtree range counts
                        Func <WeightBalancedTreeNode <RangeAndSubtreeCounts>, Nullable <Boolean>, Boolean> updateSubtreeRangeCountFunc = (node, nodeTraversedToFromLeft) =>
                        {
                            if (node == currentNode)
                            {
                                return(false);
                            }
                            if (nodeTraversedToFromLeft.HasValue)
                            {
                                if (nodeTraversedToFromLeft == true)
                                {
                                    node.Item.LeftSubtreeRangeCount -= swapNode.Item.Range.Length;
                                }
                                else
                                {
                                    node.Item.RightSubtreeRangeCount -= swapNode.Item.Range.Length;
                                }
                            }
                            return(true);
                        };
                        rangeTree.TraverseUpToNode(swapNode, updateSubtreeRangeCountFunc);
                    }

                    rangeTree.Remove(currentNode.Item);
                }
                else
                {
                    // Shorten the range on the left side
                    currentNode.Item.Range.StartValue++;
                    currentNode.Item.Range.Length--;
                }
            }
            else if (returnNumber == (currentNode.Item.Range.StartValue + currentNode.Item.Range.Length - 1))
            {
                // Shorten the range on the right side
                currentNode.Item.Range.Length--;
            }
            else
            {
                // Split the range
                var leftSideRange = new LongIntegerRange(currentNode.Item.Range.StartValue, returnNumber - currentNode.Item.Range.StartValue);
                var newNodeItem   = new RangeAndSubtreeCounts(leftSideRange, currentNode.Item.LeftSubtreeRangeCount, currentNode.Item.RightSubtreeRangeCount);
                var newNode       = new WeightBalancedTreeNode <RangeAndSubtreeCounts>(newNodeItem, null);
                currentNode.Item.Range.Length    -= (returnNumber - currentNode.Item.Range.StartValue + 1);
                currentNode.Item.Range.StartValue = returnNumber + 1;
                WeightBalancedTreeNode <RangeAndSubtreeCounts> upperNode = null;
                if (currentNode.LeftSubtreeSize > currentNode.RightSubtreeSize)
                {
                    // 'Push' the current node down right
                    if (currentNode == rangeTree.RootNode)
                    {
                        rangeTree.RootNode = newNode;
                    }
                    else
                    {
                        if (currentNode.ParentNode.LeftChildNode == currentNode)
                        {
                            currentNode.ParentNode.LeftChildNode = newNode;
                        }
                        else
                        {
                            currentNode.ParentNode.RightChildNode = newNode;
                        }
                        newNode.ParentNode = currentNode.ParentNode;
                    }
                    newNode.LeftChildNode = currentNode.LeftChildNode;
                    currentNode.LeftChildNode.ParentNode = newNode;
                    newNode.RightChildNode                 = currentNode;
                    newNode.LeftSubtreeSize                = currentNode.LeftSubtreeSize;
                    newNode.RightSubtreeSize               = 1 + currentNode.RightSubtreeSize;
                    newNode.Item.LeftSubtreeRangeCount     = currentNode.Item.LeftSubtreeRangeCount;;
                    newNode.Item.RightSubtreeRangeCount    = currentNode.Item.Range.Length + currentNode.Item.RightSubtreeRangeCount;
                    currentNode.ParentNode                 = newNode;
                    currentNode.LeftChildNode              = null;
                    currentNode.LeftSubtreeSize            = 0;
                    currentNode.Item.LeftSubtreeRangeCount = 0;
                    upperNode = newNode;
                }
                else
                {
                    // 'Push' the new node down left
                    newNode.LeftChildNode               = currentNode.LeftChildNode;
                    newNode.RightChildNode              = null;
                    newNode.LeftSubtreeSize             = currentNode.LeftSubtreeSize;
                    newNode.RightSubtreeSize            = 0;
                    newNode.Item.LeftSubtreeRangeCount  = currentNode.Item.LeftSubtreeRangeCount;
                    newNode.Item.RightSubtreeRangeCount = 0;
                    newNode.ParentNode = currentNode;
                    if (newNode.LeftChildNode != null)
                    {
                        newNode.LeftChildNode.ParentNode = newNode;
                    }
                    currentNode.LeftChildNode = newNode;
                    currentNode.LeftSubtreeSize++;
                    currentNode.Item.LeftSubtreeRangeCount += newNode.Item.Range.Length;
                    upperNode = currentNode;
                }

                // Update the subtree sizes
                Action <WeightBalancedTreeNode <RangeAndSubtreeCounts>, Nullable <Boolean> > incrementSubtreeSizeAction = (node, nodeTraversedToFromLeft) =>
                {
                    if (nodeTraversedToFromLeft.HasValue)
                    {
                        if (nodeTraversedToFromLeft.Value == true)
                        {
                            node.LeftSubtreeSize++;
                        }
                        else
                        {
                            node.RightSubtreeSize++;
                        }
                    }
                };
                rangeTree.TraverseUpFromNode(upperNode, incrementSubtreeSizeAction);
                // Balance the tree
                rangeTree.BalanceTreeUpFromNode(upperNode);
            }

            numbersGeneratedCount++;

            return(returnNumber);
        }