Exemplo n.º 1
0
        /// <summary>
        /// Attempts to add the <see cref="MNodeEntry{TValue}"/> to the specified node in the tree. If the node cannot be added the tree will create new nodes and rebalance.
        /// </summary>
        /// <param name="node">The node to add the new entry to.</param>
        /// <param name="newNodeEntry">The new node entry to add.</param>
        private void Add(MNode <int> node, MNodeEntry <int> newNodeEntry)
        {
            /*
             *  NOTE: The insertion, split, partition and promotion methods are quite complicated. If you are trying to understand this code you should
             *  really consider reading the original paper:
             *  P. Ciaccia, M. Patella, and P. Zezula. M-tree: an efficient access method for similarity search in metric spaces.
             */

            // If we are trying to insert into an internal node then we determine if the new entry resides in the ball of any
            // entry in the current node. If it does reside in a ball, then we recurse in to that entry's child node.
            // If the new entry does NOT reside in any of the balls then we picks the entry whose ball should expand the least to enclose
            // the new node entry.
            if (node.IsInternalNode)
            {
                // Anonymous type to store the entries and their distance from the new node entry.
                var entriesWithDistance = node.Entries.Select(n => new { Node = n, Distance = this.Metric(this[n.Value], this[newNodeEntry.Value]) }).ToArray();

                // This would be all the entries
                var ballsContainingEntry = entriesWithDistance.Where(d => d.Distance < d.Node.CoveringRadius).ToArray();
                MNodeEntry <int> closestEntry;

                if (ballsContainingEntry.Any())
                {
                    // New entry is currently in the region of a ball
                    closestEntry = ballsContainingEntry[ballsContainingEntry.Select(b => b.Distance).MinIndex()].Node;
                }
                else
                {
                    // The new element does not currently reside in any of the current regions balls.
                    // Since we are not in any of the balls we find which whose radius we must increase the least
                    var closestEntryIndex = entriesWithDistance.Select(d => d.Distance - d.Node.CoveringRadius).MinIndex();
                    closestEntry = entriesWithDistance[closestEntryIndex].Node;
                    closestEntry.CoveringRadius = entriesWithDistance[closestEntryIndex].Distance;
                }

                // Recurse into the closest elements subtree
                this.Add(closestEntry.ChildNode, newNodeEntry);
            }
            else
            {
                if (!node.IsFull)
                {
                    // Node is a leaf node. If the node is not full we simply add to that node.
                    if (node == this.Root)
                    {
                        node.Add(newNodeEntry);
                    }
                    else
                    {
                        newNodeEntry.DistanceFromParent = this.Metric(this.internalArray[node.ParentEntry.Value], this.internalArray[newNodeEntry.Value]);
                        node.Add(newNodeEntry);
                    }
                }
                else
                {
                    this.Split(node, newNodeEntry);
                }
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Splits a leaf node and adds the <paramref name="newEntry"/>
        /// </summary>
        /// <param name="node"></param>
        /// <param name="newEntry"></param>
        private void Split(MNode <int> node, MNodeEntry <int> newEntry)
        {
            var         nodeIsRoot       = node == this.Root;
            MNode <int> parent           = null;
            var         parentEntryIndex = -1;

            if (!nodeIsRoot)
            {
                // keep reference to parent node
                parent           = node.ParentEntry.EnclosingNode;
                parentEntryIndex = parent.Entries.IndexOf(node.ParentEntry);
                //if we are not the root, the get the parent of the current node.
            }

            // Create local copy of entries
            var entries = node.Entries.ToList();

            entries.Add(newEntry);

            var newNode = new MNode <int> {
                Capacity = this.Capacity
            };
            var promotionResult = this.Promote(entries.ToArray(), node.IsInternalNode);

            // TODO: Does not need to be an array
            node.Entries    = promotionResult.FirstPartition;
            newNode.Entries = promotionResult.SecondPartition;

            // Set child nodes of promotion objects
            promotionResult.FirstPromotionObject.ChildNode  = node;
            promotionResult.SecondPromotionObject.ChildNode = newNode;

            if (nodeIsRoot)
            {
                // if we are the root node, then create a new root and assign the promoted objects to them
                var newRoot = new MNode <int> {
                    ParentEntry = null, Capacity = this.Capacity
                };
                newRoot.AddRange(
                    new List <MNodeEntry <int> >
                {
                    promotionResult.FirstPromotionObject,
                    promotionResult.SecondPromotionObject
                });

                this.Root = newRoot;
            }
            else // we are not the root
            {
                // Set distance from parent
                if (parent == this.Root)
                {
                    promotionResult.FirstPromotionObject.DistanceFromParent = -1;
                }
                else
                {
                    promotionResult.FirstPromotionObject.DistanceFromParent =
                        this.Metric(this.internalArray[promotionResult.FirstPromotionObject.Value], this.internalArray[parent.ParentEntry.Value]);
                }

                parent.SetEntryAtIndex(parentEntryIndex, promotionResult.FirstPromotionObject);
                if (parent.IsFull)
                {
                    this.Split(parent, promotionResult.SecondPromotionObject);
                }
                else
                {
                    // Set distance from parent
                    if (parent == this.Root)
                    {
                        promotionResult.SecondPromotionObject.DistanceFromParent = -1;
                    }
                    else
                    {
                        promotionResult.SecondPromotionObject.DistanceFromParent =
                            this.Metric(this.internalArray[promotionResult.SecondPromotionObject.Value], this.internalArray[parent.ParentEntry.Value]);
                    }

                    parent.Add(promotionResult.SecondPromotionObject);
                }
            }
        }