473,804 Members | 2,191 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Stable priority queue

Hello,

I understand that an easy way to make the standard std::priority_q ueue
stable is by including an integer stamp with each node that is
incremented each time a new node is pushed into the queue. However, no
matter what reasonably-sized type I use for the stamp, eventually the
stamp will 'wrap around' and possibly cause incorrect ordering of
elements. For my application, which queues elements very quickly and
runs for an indefinite amount of time, this scenario is a real concern,
and will eventually cause incorrect results.

Is there any easy and painless way of correcting this order-stamp
wraparound problem?
Aaron W. LaFramboise

Jul 22 '05
38 5807
"Kevin Goodsell" <us************ *********@never box.com> wrote in message
Siemel Naran wrote:

What is a stable priority queue?


I'm guessing items of the same priority come out in the order they were
inserted.


Then maybe we can use the protected member 'c'. The standard says

template <class T, class Container = std::vector<T>, ...>
class priority_queue {
protected:
Container c;
...
};

So we could derive our own class from priority_queue, and add read and write
functions that read directly into and out of 'c'. If deriving from a
non-polymorphic class bothers us, we can use private inheritance with using
declarations.

We could get stable queue's and stacks too.

Anyway, what is a priority_queue? Is it just like a queue, except that
items with the highest priority get retrieved by top() and pop() first? How
do they achieve O(lg(N)) for both push and pop (ie. how does the heap
work?).

Jul 22 '05 #11
"Aaron W. LaFramboise" <aa********@c ox-internet.com> wrote:
I understand that an easy way to make the standard std::priority_q ueue
stable is by including an integer stamp with each node that is
incremented each time a new node is pushed into the queue. However, no
matter what reasonably-sized type I use for the stamp, eventually the
stamp will 'wrap around' and possibly cause incorrect ordering of
elements.
Use a 128-bit integer (eg. made out of four longs): I doubt that this one
will overflow on any currently available machine before the sun (note, not
the SUN) cheases to exist. Even if you account for faster processors you
should be on the safe side.
Is there any easy and painless way of correcting this order-stamp
wraparound problem?


It is hard to tell what you are really doing. If a O(log n) insert and
extract is all you need, in particular if you don't need to modify
priorities, a simple alternative is to use a map rather than a proper
priority queue: the map's key is the priority and the value is a queue
of items with the same priority. All you need to do is to provide a
suitable interface:

- empty() -> 'm.empty()'
- top() -> 'm.begin()->second.front() '
- pop() -> 'm.begin()->second.pop() ;
if (m.begin()->second.empty() ) m.erase(m.begin ())'
- push(e) -> 'm[e.priority].push(e)'

I don't think that any of the approach to priority queues is capable to
deal stability. At least, a vanilla implementation of all priority queues
I know (d-heap, priority queue, radix heap, splay heap) will not be stable.
I don't think that any of those heaps can be made inherently stable based
only on its operation, ie. you would always need some additional hack like
the sequence number.
--
<mailto:di***** ******@yahoo.co m> <http://www.dietmar-kuehl.de/>
<www.contendix. com> - Software Development & Consulting
Jul 22 '05 #12
"Aaron W. LaFramboise" <aa********@c ox-internet.com> wrote:
I understand that an easy way to make the standard std::priority_q ueue
stable is by including an integer stamp with each node that is
incremented each time a new node is pushed into the queue. However, no
matter what reasonably-sized type I use for the stamp, eventually the
stamp will 'wrap around' and possibly cause incorrect ordering of
elements.
Use a 128-bit integer (eg. made out of four longs): I doubt that this one
will overflow on any currently available machine before the sun (note, not
the SUN) cheases to exist. Even if you account for faster processors you
should be on the safe side.
Is there any easy and painless way of correcting this order-stamp
wraparound problem?


It is hard to tell what you are really doing. If a O(log n) insert and
extract is all you need, in particular if you don't need to modify
priorities, a simple alternative is to use a map rather than a proper
priority queue: the map's key is the priority and the value is a queue
of items with the same priority. All you need to do is to provide a
suitable interface:

- empty() -> 'm.empty()'
- top() -> 'm.begin()->second.front() '
- pop() -> 'm.begin()->second.pop() ;
if (m.begin()->second.empty() ) m.erase(m.begin ())'
- push(e) -> 'm[e.priority].push(e)'

I don't think that any of the approach to priority queues is capable to
deal stability. At least, a vanilla implementation of all priority queues
I know (d-heap, priority queue, radix heap, splay heap) will not be stable.
I don't think that any of those heaps can be made inherently stable based
only on its operation, ie. you would always need some additional hack like
the sequence number.
--
<mailto:di***** ******@yahoo.co m> <http://www.dietmar-kuehl.de/>
<www.contendix. com> - Software Development & Consulting
Jul 22 '05 #13
"Dietmar Kuehl" <di***********@ yahoo.com> wrote
Use a 128-bit integer (eg. made out of four longs): I
doubt that this one will overflow on any currently available
machine before the sun (note, not the SUN) cheases to exist.
Even if you account for faster processors you should be
on the safe side.


Gaa! Do people no longer do basic back-of-the envelope calculations?

If a 5GHz computer had an integer increment that took only one CPU cycle and
did NOTHING but increment a 64-bit int 5 billion times per second 24-hours a
day without interruption, it would take 117 _YEARS_ before you ran out of
numbers!!! If you throw in the obligatory jump to implement the loop and
made the jump also take a single CPU cycle, the duration leaps to a whopping
234 years.

The most generous thing I can say about using a 128-bit integer for this
purpose is that it's absurdly over-engineered. There's a lot worse that
could be said about the idea.

Claudio Puviani
Jul 22 '05 #14
"Dietmar Kuehl" <di***********@ yahoo.com> wrote
Use a 128-bit integer (eg. made out of four longs): I
doubt that this one will overflow on any currently available
machine before the sun (note, not the SUN) cheases to exist.
Even if you account for faster processors you should be
on the safe side.


Gaa! Do people no longer do basic back-of-the envelope calculations?

If a 5GHz computer had an integer increment that took only one CPU cycle and
did NOTHING but increment a 64-bit int 5 billion times per second 24-hours a
day without interruption, it would take 117 _YEARS_ before you ran out of
numbers!!! If you throw in the obligatory jump to implement the loop and
made the jump also take a single CPU cycle, the duration leaps to a whopping
234 years.

The most generous thing I can say about using a 128-bit integer for this
purpose is that it's absurdly over-engineered. There's a lot worse that
could be said about the idea.

Claudio Puviani
Jul 22 '05 #15

"Claudio Puviani" <pu*****@hotmai l.com> wrote in message
news:0i******** *************@n ews4.srv.hcvlny .cv.net...
"Dietmar Kuehl" <di***********@ yahoo.com> wrote
Use a 128-bit integer (eg. made out of four longs): I
doubt that this one will overflow on any currently available
machine before the sun (note, not the SUN) cheases to exist.
Even if you account for faster processors you should be
on the safe side.
Gaa! Do people no longer do basic back-of-the envelope calculations?

If a 5GHz computer had an integer increment that took only one CPU cycle

and did NOTHING but increment a 64-bit int 5 billion times per second 24-hours a day without interruption, it would take 117 _YEARS_ before you ran out of
numbers!!! If you throw in the obligatory jump to implement the loop and
made the jump also take a single CPU cycle, the duration leaps to a whopping 234 years.

The most generous thing I can say about using a 128-bit integer for this
purpose is that it's absurdly over-engineered. There's a lot worse that
could be said about the idea.

Claudio Puviani


My envelopes must be old. They only support 32-bit math. :-)

-Howard

Jul 22 '05 #16

"Claudio Puviani" <pu*****@hotmai l.com> wrote in message
news:0i******** *************@n ews4.srv.hcvlny .cv.net...
"Dietmar Kuehl" <di***********@ yahoo.com> wrote
Use a 128-bit integer (eg. made out of four longs): I
doubt that this one will overflow on any currently available
machine before the sun (note, not the SUN) cheases to exist.
Even if you account for faster processors you should be
on the safe side.
Gaa! Do people no longer do basic back-of-the envelope calculations?

If a 5GHz computer had an integer increment that took only one CPU cycle

and did NOTHING but increment a 64-bit int 5 billion times per second 24-hours a day without interruption, it would take 117 _YEARS_ before you ran out of
numbers!!! If you throw in the obligatory jump to implement the loop and
made the jump also take a single CPU cycle, the duration leaps to a whopping 234 years.

The most generous thing I can say about using a 128-bit integer for this
purpose is that it's absurdly over-engineered. There's a lot worse that
could be said about the idea.

Claudio Puviani


My envelopes must be old. They only support 32-bit math. :-)

-Howard

Jul 22 '05 #17
Siemel Naran wrote:
Then maybe we can use the protected member 'c'.
'std::priority_ queue' actually just uses 'std::make_heap ', etc. Anyway,
accessing the member directly is no good: the internal organization of
the heap essentially makes immediate stability without some tool
impossible.
Anyway, what is a priority_queue? Is it just like a queue, except that
items with the highest priority get retrieved by top() and pop() first?
This is the interface description: It has operations 'empty()', 'top()',
'pop()', and 'push()' like a 'std::stack' and what a 'std::queue' should
have, too (unfortunately, the latter uses 'front()' instead of 'top()').
However, the internal organization of the priority queue is a little bit
more involved.
How do they achieve O(lg(N)) for both push and pop (ie. how does the heap
work?).


The essential idea of several heap structure is to maintain the heap
property: the heap is viewed as a tree and each tree has its minimum
element in the root (reverse the comparison and it has the maximum
element in the root; this does not really matter as long as you stick
with one definition - I will use the minimum). In the container stored
in the 'std::priority_ queue' a binary tree is implicitly stored: the
element with index '0' is the root. The children of a node with index
'n' are the nodes with '2 * (n + 1) - 1' and '2 * (n + 1)'. The parent
of the node 'n' is the node '(n - 1) / 2' (I guess, I got the numbers
wrong somewhere; just draw a picture and number the nodes and things
should be relatively easy to work out). In any case, the tree is fully
balanced.

Inserting into a heap is pretty simple: you add a node to the lowest
level and exchange it with its parent if the parent has a bigger key.
This far, the representation of the tree does not at all matter and
other heaps, eg. a Fibonacci-heap, use a different structure than an
array to hold a heap although they also maintain the heap property.
Since the heap in the array is balanced, insertion takes at most
O(log n) steps.

Extracting from a heap, is also simple: you simply exchange the root
with the last element and bubble down the root again: check which of
the child nodes is the smaller one and exchange the node with this
child until the node is smaller than both children. This again takes
at most O(log n) steps.

This form of heap is called a "d-heap" in its general form where "d"
is the arity of the tree: the same approach works with non-binary
trees, too, although the computation of child and parent nodes changes
slightly. When I compared different priority queues, I found that
"d == 5" had the best performance for integer values stored in the
heap. An even bigger 'd' is probably better if the nodes itself become
bigger. Of course, this was based on a few tests with my own
implementation and probably does not hold up to a scientific analysis.

Other forms of priority queues provide additional capabilities, eg.
ability to change the key for the priority fast. Of some interest in
this direction are Fibonacci-heaps which can do changes of the keys
in such a form that it becomes amortized constant, at least when using
it for Dijkstra's algorithms for finding shortest paths (I don't
remember the details; it is possible that it is amortized constant in
general applications, too).
--
<mailto:di***** ******@yahoo.co m> <http://www.dietmar-kuehl.de/>
<http://www.contendix.c om> - Software Development & Consulting
Jul 22 '05 #18
Siemel Naran wrote:
Then maybe we can use the protected member 'c'.
'std::priority_ queue' actually just uses 'std::make_heap ', etc. Anyway,
accessing the member directly is no good: the internal organization of
the heap essentially makes immediate stability without some tool
impossible.
Anyway, what is a priority_queue? Is it just like a queue, except that
items with the highest priority get retrieved by top() and pop() first?
This is the interface description: It has operations 'empty()', 'top()',
'pop()', and 'push()' like a 'std::stack' and what a 'std::queue' should
have, too (unfortunately, the latter uses 'front()' instead of 'top()').
However, the internal organization of the priority queue is a little bit
more involved.
How do they achieve O(lg(N)) for both push and pop (ie. how does the heap
work?).


The essential idea of several heap structure is to maintain the heap
property: the heap is viewed as a tree and each tree has its minimum
element in the root (reverse the comparison and it has the maximum
element in the root; this does not really matter as long as you stick
with one definition - I will use the minimum). In the container stored
in the 'std::priority_ queue' a binary tree is implicitly stored: the
element with index '0' is the root. The children of a node with index
'n' are the nodes with '2 * (n + 1) - 1' and '2 * (n + 1)'. The parent
of the node 'n' is the node '(n - 1) / 2' (I guess, I got the numbers
wrong somewhere; just draw a picture and number the nodes and things
should be relatively easy to work out). In any case, the tree is fully
balanced.

Inserting into a heap is pretty simple: you add a node to the lowest
level and exchange it with its parent if the parent has a bigger key.
This far, the representation of the tree does not at all matter and
other heaps, eg. a Fibonacci-heap, use a different structure than an
array to hold a heap although they also maintain the heap property.
Since the heap in the array is balanced, insertion takes at most
O(log n) steps.

Extracting from a heap, is also simple: you simply exchange the root
with the last element and bubble down the root again: check which of
the child nodes is the smaller one and exchange the node with this
child until the node is smaller than both children. This again takes
at most O(log n) steps.

This form of heap is called a "d-heap" in its general form where "d"
is the arity of the tree: the same approach works with non-binary
trees, too, although the computation of child and parent nodes changes
slightly. When I compared different priority queues, I found that
"d == 5" had the best performance for integer values stored in the
heap. An even bigger 'd' is probably better if the nodes itself become
bigger. Of course, this was based on a few tests with my own
implementation and probably does not hold up to a scientific analysis.

Other forms of priority queues provide additional capabilities, eg.
ability to change the key for the priority fast. Of some interest in
this direction are Fibonacci-heaps which can do changes of the keys
in such a form that it becomes amortized constant, at least when using
it for Dijkstra's algorithms for finding shortest paths (I don't
remember the details; it is possible that it is amortized constant in
general applications, too).
--
<mailto:di***** ******@yahoo.co m> <http://www.dietmar-kuehl.de/>
<http://www.contendix.c om> - Software Development & Consulting
Jul 22 '05 #19
Claudio Puviani wrote:
"Dietmar Kuehl" <di***********@ yahoo.com> wrote
Use a 128-bit integer (eg. made out of four longs): I
doubt that this one will overflow on any currently available
machine before the sun (note, not the SUN) cheases to exist.
Even if you account for faster processors you should be
on the safe side.
Gaa! Do people no longer do basic back-of-the envelope calculations?


I had to study mathematics because I'm notoriously bad at
calculating things and mathematics is the only more or less
technical topic where you don't need to do calculations - you
only prove that they can be done. With 128-bit I'm definitely
on the safe side...
If a 5GHz computer had an integer increment that took only one CPU cycle
and did NOTHING but increment a 64-bit int 5 billion times per second
24-hours a day without interruption, it would take 117 _YEARS_ before you
ran out of numbers!!! If you throw in the obligatory jump to implement the
loop and made the jump also take a single CPU cycle, the duration leaps to
a whopping 234 years.
Well, this computation seems reasonable (even though I'm not fit
to verifying it). Actually, the OP mentioned that this approach
does not work for him and all I wanted to do in this area is to
show that it is just a question of how many bits you spend to
make the approach work.
The most generous thing I can say about using a 128-bit integer for this
purpose is that it's absurdly over-engineered. There's a lot worse that
could be said about the idea.


To me, the difference of using 128-bits or 64-bits is not really
there: yes, I need twice as much memory but who cares? The template
class for doing simple arithmetics does not care how many (unsigned)
longs it takes for its representation and there is no portable
64-bit integer. Over-engineered? I'd say I was too lazy to do a more
precise computation.
--
<mailto:di***** ******@yahoo.co m> <http://www.dietmar-kuehl.de/>
<http://www.contendix.c om> - Software Development & Consulting
Jul 22 '05 #20

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

38
671
by: Aaron W. LaFramboise | last post by:
Hello, I understand that an easy way to make the standard std::priority_queue stable is by including an integer stamp with each node that is incremented each time a new node is pushed into the queue. However, no matter what reasonably-sized type I use for the stamp, eventually the stamp will 'wrap around' and possibly cause incorrect ordering of elements. For my application, which queues elements very quickly and runs for an...
5
13251
by: Dan H. | last post by:
Hello, I have implemented a C# priority queue using an ArrayList. The objects being inserted into the priority queue are being sorted by 2 fields, Time (ulong) and Priority (0-100). When I enqueue, I do a binary search on where to put the object and then insert using that index and arraylist.insert(index, obj) The bottom of my arraylist is always my lowest, therefore, dequeueing is very fast and effecient.
0
9715
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9595
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10603
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10353
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
0
10099
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
9176
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7643
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
1
4314
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
3
3003
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.