24

I want to convert a recursive function to a iterative one. What I normally do is, I initialize a queue, put the first job into queue. Then in a while loop I consume jobs from queue and add new ones to the queue. If my recursive function calls itself multiple times (e.g walking a tree with many branches) multiple jobs are added. Pseudo code:

queue = new Queue();
queue.put(param);
result = 0;

while (!queue.isEmpty()) {
    param = queue.remove();
    // process param and obtain new param(s)
    // change result
    queue.add(param1);
    queue.add(param2);
}

return result;

I cannot find any queue like structure in MATLAB though. I can use vector to simulate queue where adding 3 to queue is like:

a = [a 3]

and removing element is

val = a(1);
a(1) = [];

If I got the MATLAB way right, this method will be a performance killer.

Is there a sane way to use a queue in MATLAB?

What about other data structures?

nimcap
  • 10,062
  • 15
  • 61
  • 69

7 Answers7

36

If you insist on using proper data structures, you can use Java from inside MATLAB:

import java.util.LinkedList
q = LinkedList();
q.add('item1');
q.add(2);
q.add([3 3 3]);
item = q.remove();
q.add('item4');
Amro
  • 123,847
  • 25
  • 243
  • 454
  • 1
    Well, I don't insist, I am trying to find my way through MATLAB. Maybe I do not need queue at all to solve my problem, maybe there is a MATLAB way to do this. – nimcap Nov 10 '10 at 09:46
  • 1
    +1 Would this be the best? Maybe... Anyway it's simple and has its elegancy. – Giuliano Aug 09 '12 at 19:19
  • 5
    For Octave compatibility (if you have the java package installed,) just replace the first 2 lines with `q = javaObject('java.util.LinkedList')`. – tmandry Apr 22 '13 at 04:20
  • @tmandry: thanks, `javaObject` actually works in both MATLAB and Octave – Amro Apr 22 '13 at 08:12
  • 1
    If speed is a requirement, for some reason Matlab is MUCH faster if you call add(a, 'item1') and remove(q) and the like – Cramer Jun 21 '13 at 02:59
  • @Cramer: I agree that function notation is sometimes faster than dot notation, this has to do with the way MATLAB dispatches and calls methods: http://www.mathworks.com/help/matlab/matlab_prog/calling-object-methods.html , http://www.mathworks.com/help/matlab/matlab_oop/ordinary-methods.html#brd2n2o-2 . See this for an actual comparison of the the versions (among other things): http://stackoverflow.com/a/1745686/97160 – Amro Jun 21 '13 at 03:24
9

Ok, here's a quick-and-dirty, barely tested implementation using a MATLAB handle class. If you're only storing scalar numeric values, you could use a double array for "elements" rather than a cell array. No idea about performance.

classdef Queue < handle
    properties ( Access = private )
        elements
        nextInsert
        nextRemove
    end

    properties ( Dependent = true )
        NumElements
    end

    methods
        function obj = Queue
            obj.elements = cell(1, 10);
            obj.nextInsert = 1;
            obj.nextRemove = 1;
        end
        function add( obj, el )
            if obj.nextInsert == length( obj.elements )
                obj.elements = [ obj.elements, cell( 1, length( obj.elements ) ) ];
            end
            obj.elements{obj.nextInsert} = el;
            obj.nextInsert = obj.nextInsert + 1;
        end
        function el = remove( obj )
            if obj.isEmpty()
                error( 'Queue is empty' );
            end
            el = obj.elements{ obj.nextRemove };
            obj.elements{ obj.nextRemove } = [];
            obj.nextRemove = obj.nextRemove + 1;
            % Trim "elements"
            if obj.nextRemove > ( length( obj.elements ) / 2 )
                ntrim = fix( length( obj.elements ) / 2 );
                obj.elements = obj.elements( (ntrim+1):end );
                obj.nextInsert = obj.nextInsert - ntrim;
                obj.nextRemove = obj.nextRemove - ntrim;
            end
        end
        function tf = isEmpty( obj )
            tf = ( obj.nextRemove >= obj.nextInsert );
        end
        function n = get.NumElements( obj )
            n = obj.nextInsert - obj.nextRemove;
        end
    end
end
Edric
  • 23,676
  • 2
  • 38
  • 40
  • 1
    +1: That's certainly the advanced way to go. If this is too advanced, just use a vector with head and tail pointers as @Edric does. – High Performance Mark Nov 10 '10 at 09:50
  • Doubling the size of the queue when full could lead to matlab memory exhaustion quickly, because of Matlab Heap fragmentation. Nice thing about a class is that you can change that to be a constant-size add. – Marc Nov 10 '10 at 14:30
  • Apparently this is the way to go for performance in pure matlab, since both queue(1)=[] and queue=queue(2:end) [perform poorly](http://www.alecjacobson.com/weblog/?p=3933) – Pepe Mandioca Jul 18 '16 at 21:03
5
  1. Is a recursive solution really so bad? (always examine your design first).
  2. File Exchange is your friend. (steal with pride!)
  3. Why bother with the trouble of a proper Queue or a class - fake it a bit. Keep it simple:

q = {};
head = 1;
q{head} = param;
result = 0;
while (head<=numel(q))
%process param{head} and obtain new param(s) head = head + 1; %change result q{end+1} = param1; q{end+1} = param2; end %loop over q return result;

If the performance suffers from adding at the end too much - add in chunks:

chunkSize = 100;
chunk = cell(1, chunkSize);
q = chunk;
head = 1;
nextLoc = 2;
q{head} = param;
result = 0;
while (head<endLoc)        
    %process param{head} and obtain new param(s)
    head = head + 1;
    %change result
    if nextLoc > numel(q);
        q = [q chunk];
    end
    q{nextLoc} = param1;
    nextLoc = nextLoc + 1;
    q{end+1} = param2;
    nextLoc = nextLoc + 1;
end %loop over q
 return result;

A class is certainly more elegant and reusable - but fit the tool to the task.

Marc
  • 3,259
  • 4
  • 30
  • 41
  • 1
    yes, recursive solution tends to be worse in most scenarios http://stackoverflow.com/questions/4137905/how-to-modify-an-array-in-function/4138188#4138188. But the definitive answer can be only given comparing performance of all approaches. Still +1 for keeping the solution simple! – Mikhail Poda Nov 10 '10 at 15:17
  • Wrapping this in a class seems like a good idea, but would probably [hurt performance](http://www.alecjacobson.com/weblog/?p=3929) – Alec Jacobson May 07 '14 at 21:37
1

If you can do with a FIFO queue of predefined size without the need for simple direct access, you can simply use the modulo operator and some counter variable:

myQueueSize = 25;                  % Define queue size
myQueue = zeros(1,myQueueSize);    % Initialize queue

k = 1                              % Counter variable
while 1                            
    % Do something, and then
    % Store some number into the queue in a FIFO manner
    myQueue(mod(k, myQueueSize)+1) = someNumberToQueue;

    k= k+1;                       % Iterate counter
end

This approach is super simple, but has the downside of not being as easily accessed as your typical queue. In other words, the newest element will always be element k, not element 1 etc.. For some applications, such as FIFO data storage for statistical operations, this is not necessarily a problem.

Tormod Haugene
  • 3,538
  • 2
  • 29
  • 47
1

Use this code, save the code as a m file, and use the functions such q.pop() etc. this is the original code with some modifications:

properties (Access = private)
    buffer      % a cell, to maintain the data
    beg         % the start position of the queue
    rear        % the end position of the queue
                % the actually data is buffer(beg:rear-1)
end

properties (Access = public)
    capacity    % ص»µؤبفء؟£¬µ±بفء؟²»¹»ت±£¬بفء؟ہ©³نخھ2±¶،£
end

methods
    function obj = CQueue(c) % ³ُت¼»¯
        if nargin >= 1 && iscell(c)
            obj.buffer = [c(:); cell(numel(c), 1)];
            obj.beg = 1;
            obj.rear = numel(c) + 1;
            obj.capacity = 2*numel(c);
        elseif nargin >= 1
            obj.buffer = cell(100, 1);
            obj.buffer{1} = c;
            obj.beg = 1;
            obj.rear = 2;
            obj.capacity = 100;                
        else
            obj.buffer = cell(100, 1);
            obj.capacity = 100;
            obj.beg = 1;
            obj.rear = 1;
        end
    end

    function s = size(obj) % ¶سءذ³¤¶ب
        if obj.rear >= obj.beg
            s = obj.rear - obj.beg;
        else
            s = obj.rear - obj.beg + obj.capacity;
        end
    end

    function b = isempty(obj)   % return true when the queue is empty
        b = ~logical(obj.size());
    end

    function s = empty(obj) % clear all the data in the queue
        s = obj.size();
        obj.beg = 1;
        obj.rear = 1;
    end

    function push(obj, el) % ر¹بëذآشھثطµ½¶سخ²
        if obj.size >= obj.capacity - 1
            sz = obj.size();
            if obj.rear >= obj.beg 
                obj.buffer(1:sz) = obj.buffer(obj.beg:obj.rear-1);                    
            else
                obj.buffer(1:sz) = obj.buffer([obj.beg:obj.capacity 1:obj.rear-1]);
            end
            obj.buffer(sz+1:obj.capacity*2) = cell(obj.capacity*2-sz, 1);
            obj.capacity = numel(obj.buffer);
            obj.beg = 1;
            obj.rear = sz+1;
        end
        obj.buffer{obj.rear} = el;
        obj.rear = mod(obj.rear, obj.capacity) + 1;
    end

    function el = front(obj) % ·µ»ط¶ست×شھثط
        if obj.rear ~= obj.beg
            el = obj.buffer{obj.beg};
        else
            el = [];
            warning('CQueue:NO_DATA', 'try to get data from an empty queue');
        end
    end

    function el = back(obj) % ·µ»ط¶سخ²شھثط            

       if obj.rear == obj.beg
           el = [];
           warning('CQueue:NO_DATA', 'try to get data from an empty queue');
       else
           if obj.rear == 1
               el = obj.buffer{obj.capacity};
           else
               el = obj.buffer{obj.rear - 1};
           end
        end

    end

    function el = pop(obj) % µ¯³ِ¶ست×شھثط
        if obj.rear == obj.beg
            error('CQueue:NO_Data', 'Trying to pop an empty queue');
        else
            el = obj.buffer{obj.beg};
            obj.beg = obj.beg + 1;
            if obj.beg > obj.capacity, obj.beg = 1; end
        end             
    end

    function remove(obj) % اه؟ص¶سءذ
        obj.beg = 1;
        obj.rear = 1;
    end

    function display(obj) % دشت¾¶سءذ
        if obj.size()
            if obj.beg <= obj.rear 
                for i = obj.beg : obj.rear-1
                    disp([num2str(i - obj.beg + 1) '-th element of the stack:']);
                    disp(obj.buffer{i});
                end
            else
                for i = obj.beg : obj.capacity
                    disp([num2str(i - obj.beg + 1) '-th element of the stack:']);
                    disp(obj.buffer{i});
                end     
                for i = 1 : obj.rear-1
                    disp([num2str(i + obj.capacity - obj.beg + 1) '-th element of the stack:']);
                    disp(obj.buffer{i});
                end
            end
        else
            disp('The queue is empty');
        end
    end

    function c = content(obj) % ب،³ِ¶سءذشھثط
        if obj.rear >= obj.beg
            c = obj.buffer(obj.beg:obj.rear-1);                    
        else
            c = obj.buffer([obj.beg:obj.capacity 1:obj.rear-1]);
        end
    end
end end

Reference: list, queue, stack Structures in Matlab

moksef
  • 344
  • 1
  • 5
  • 17
  • 1
    The [File Exchange](http://www.mathworks.com/matlabcentral/linkexchange/) contains community-contributed code, not official MathWorks solutions (could be misunderstood from your statement above) – Amro Aug 22 '14 at 16:22
1

I had a need for queue like data structure as well.

Fortunately I had a limited number of elements (n).

They all get into queue at some point but only once.

If you situation is similar you can adapt the simple algorithm using fixed size array and 2 indices.

queue  = zeros( n, 1 );
firstq = 1;
lastq  = 1;

while( lastq >= firstq && firstq <= n )
    i = queue( firstq );    % pull first element from the queue
                            % you do not physically remove it from an array,
                            % thus saving time on memory access
    firstq = firstq + 1;

    % % % % % % % % % % % % % WORKER PART HERE
    % do stuff

    %
    % % % % % % % % % % % % % % % % % % % % %

    queue( lastq ) = j;     % push element to the end of the queue
    lastq = lastq + 1;      % increment index

end;
user3666197
  • 1
  • 6
  • 50
  • 92
Antonm
  • 11
  • 1
0

In the case where you need a queue only to store vectors (or scalars), then it is not difficult to use a matrix along with the circshift() function to implement a basic queue with a fixed length.

% Set the parameters of our queue
n = 4; % length of each vector in queue
max_length = 5;

% Initialize a queue of length of nx1 vectors 
queue = NaN*zeros(n, max_length);
queue_length = 0;

To push:

queue = circshift(queue, 1, 2); % Move each column to the right
queue(:,1) = rand(n, 1); % Add new vector to queue
queue_length = min(max_length, queue_length + 1); 

To pop:

result = queue(:,last)
queue(:, last) = NaN;
queue_length = max(1, queue_length - 1);
Paul Wintz
  • 2,542
  • 1
  • 19
  • 33