Преобразуйте целое число в общий базовый Matlab

Я пытаюсь преобразовать целое число k с основанием 10 в целое число с основанием q, но не стандартным способом. Во-первых, я хотел бы, чтобы мой результат был вектором (или строкой «a, b, c,...», чтобы ее можно было преобразовать в вектор, но не «abc...»). Самое главное, я бы хотел, чтобы каждая «цифра» была в базе 10. В качестве примера предположим, что у меня есть число 23 (в базе 10), и я хочу преобразовать его в базу 12. Это будет 1B в стандартной нотации 1,...,9,A,B; однако я хочу, чтобы он вышел как [1, 11]. Меня интересуют только числа k с 0 \le k \le n^q - 1, где n фиксировано заранее.

Иными словами, я хочу найти коэффициенты a(r) такие, что k = \sum_{r=0}^{n-1} a(r) q^r, где каждый a(r) находится в десятичной системе счисления. (Обратите внимание, что 0 \le a(r) \le q-1.)

Я знаю, что мог бы сделать это с помощью цикла for - сейчас я изо всех сил пытаюсь получить точную формулу! -- но я хочу сделать это векторизованным или с быстрой внутренней функцией.

Однако я хочу, чтобы n было большим, поэтому я бы предпочел более быстрый способ, чем этот. (Конечно, я мог бы изменить это на цикл parfor или сделать это на графическом процессоре; это нецелесообразно для моей текущей ситуации, поэтому я бы предпочел более прямую версию.)

Я просмотрел такие вещи, как dec2base, num2str, str2num, base2dec и т. д., но безуспешно. Любое предложение будет высоко оценено.

Что касается скорости и пространства, любое предварительное выделение целых чисел в диапазоне [0, q-1] или подобном тоже было бы хорошо.

Чтобы было ясно, я ищу алгоритм, который работает для любых q и n, преобразуя любое число в диапазоне [0, q ^ n - 1].


person Sam OT    schedule 18.11.2016    source источник
comment
floor, ^ и / уже векторизованы. Петля не нужна.   -  person excaza    schedule 18.11.2016
comment
Ах, конечно они! Конечно, мой цикл for - ерунда и вообще не дает того, что я хочу =P - позвольте мне просто изменить это...   -  person Sam OT    schedule 18.11.2016


Ответы (1)


Вы можете использовать dec2base и заменить символы цифрами:

x = 23;
b = 12;
[~, result] = ismember(dec2base(x,b), ['0':'9' 'A':'Z']);
result = result -1;

дает

>> result
result =
     1    11

Это работает только для базы до 36 из-за dec2base ограничения.


Для любой базы (возможно, выше 36) вам необходимо выполнить преобразование вручную. Однажды я написал для этого функцию base2base (это, по сути, длинное деление). Число должно быть введено как вектор цифр в исходной базе, поэтому сначала вам нужно dec2base(...,10). Например:

x = 125;
b = 6;
result = base2base(dec2base(x,10), '0':'9', b); % origin nunber, origin base, target base

дает

result =
     3     2     5

Или, если вам нужно указать количество цифр:

x = 125;
b = 6;
d = 5;
result = base2base(dec2base(x,10), '0':'9', b, d)
result =
     0     0     3     2     5

EDIT (15 августа 2017 г.): исправлены две ошибки: обработка ввода, состоящего из одних нулей (спасибо @Sanchises на заметку) и, при необходимости, корректно дополнив вывод «нулями».

function Z = base2base(varargin)
% Three inputs: origin array, origin base, target base
%   If a base is specified by a number, say b, the digits are [0,1,...,d-1].
% The base can also be directly an array with the digits
%   Fourth input, optional: how many digits the output should have as a
% minimum (padding with leading zeros, i.e with the first digit)
%   Non-valid digits in origin array are discarded.
%   It works with cell arrays. In this case it gives a matrix in which each
% row is padded with leading zeros if needed
%   If the base is specified as a number, digits are numbers, not
% characters as in `dec2base` and `base2dec`

if ~iscell(varargin{1}), varargin{1} = varargin(1); end
if numel(varargin{2})>1, ax = varargin{2}; bx=numel(ax); else bx = varargin{2}; ax = 0:bx-1; end
if numel(varargin{3})>1, az = varargin{3}; bz=numel(az); else bz = varargin{3}; az = 0:bz-1; end
Z = cell(size(varargin{1}));
for c = 1:numel(varargin{1})
    x = varargin{1}{c}; [valid, x] = ismember(x,ax); x = x(valid)-1;
    if ~isempty(x) && ~any(x) % Non-empty input, all zeros
        z = 0;
    elseif ~isempty(x) % Non-empty input, at least a nonzero
        z = NaN(1,ceil(numel(x)*log2(bx)/log2(bz))); done_outer = false;
        n = 0;
        while ~done_outer
            n = n + 1;
            x = [0 x(find(x,1):end)];
            y = NaN(size(x)); done_inner = false;
            m = 0;
            while ~done_inner
                m = m + 1;
                t = x(1)*bx+x(2);
                r = mod(t, bz); q = (t-r)/bz;
                y(m) = q; x = [r x(3:end)];
                done_inner = numel(x) < 2;
            end
            y = y(1:m);
            z(n) = r; x = y; done_outer = ~any(x);
        end
        z = z(n:-1:1);
    else % Empty input
        z = []; % output will be empty (unless user has required left-padding) with the
       % appropriate class
    end
    if numel(varargin)>=4 && numel(z)<varargin{4}, z = [zeros(1,varargin{4}-numel(z)) z]; end
    % left-pad if required by user
    Z{c} = z;
end
L = max(cellfun(@numel, Z));
Z = cellfun(@(x) [zeros(1, L-numel(x)) x], Z, 'uniformoutput', false); % left-pad so that
% result will be a matrix
Z = vertcat(Z{:});
Z = az(Z+1);
person Luis Mendo    schedule 18.11.2016
comment
Извините, я хочу сделать это для общего q, так что это не работает для меня. Я поясню это в вопросе. - person Sam OT; 18.11.2016
comment
Кроме того, использование вашего алгоритма с x = 125 и q = 16 (я полагаю, вы имели в виду, что работает только для q до 36; x может быть любым) дает результат [7, -1], когда он должен быть [7, 13]... - person Sam OT; 18.11.2016
comment
@SamT Извините, была опечатка. Исправлено сейчас. Для основания выше 36 вам нужно сделать это вручную (по сути, это длинное деление). Однажды я написал для этого функцию; позвольте мне найти это - person Luis Mendo; 18.11.2016
comment
Отредактировано с помощью моей функции base2base - person Luis Mendo; 18.11.2016
comment
Отлично, спасибо. Я реализую это в понедельник :) - похоже, это тоже должно быть довольно быстро: не очень длинный цикл for. - person Sam OT; 18.11.2016
comment
@SamT Когда я писал это, я не думал о скорости кода. Я не ожидаю, что это будет особенно быстро с этими двумя вложенными циклами while. Возможно, вы могли бы увеличить скорость, удалив некоторые ненужные части, такие как поддержка массива ячеек. - person Luis Mendo; 18.11.2016
comment
Я не слишком беспокоюсь о скорости. Поскольку мы говорим о n^q, я все равно не могу считать n очень большим! - person Sam OT; 18.11.2016
comment
Ах, к сожалению, это не работает для меня: мне нужно иметь 0 в начале. Например, 1 должно быть (0,0,...,0,1). Я понимаю, что не ясно выразился. С dec2base (который я могу использовать только для q ‹= 10) я могу использовать dec2base(x,q,n), чтобы заставить строку иметь n записей, добавляя 0 в начале по мере необходимости. Спасибо за вашу помощь в любом случае - person Sam OT; 21.11.2016
comment
@SamT Моя функция имеет четвертый необязательный параметр, который делает это. я добавил пример - person Luis Mendo; 21.11.2016