2016-11-18 3 views
2

J'essaie de convertir un entier de base 10 en un entier de base-q, mais pas de la manière standard. Premièrement, je voudrais que mon résultat soit un vecteur (ou une chaîne 'a, b, c, ...' pour qu'il puisse être converti en vecteur, mais pas 'abc ...'). Plus important encore, j'aimerais que chaque «chiffre» soit en base 10. A titre d'exemple, supposons que j'ai le numéro 23 (en base 10) et que je veuille le convertir en base 12. Ce serait 1B dans la notation standard 1, ..., 9, A, B; Cependant, je veux que cela sorte comme [1, 11]. Je ne m'intéresse qu'aux nombres k avec 0 \ le k \ le n^q - 1, où n est fixé à l'avance.Convertir un entier en base mathématique Matlab

Autrement dit, je voudrais trouver des coefficients a (r) de telle sorte que k = \sum_{r=0}^{n-1} a(r) q^r où chacun un (r) est dans la base-10. (Notez que 0 \ le a (r) \ le q-1.)

Je sais que je pourrais le faire avec une boucle for - luttant pour obtenir la formule exacte pour le moment! - mais je veux le faire vectorisé, ou avec une fonction interne rapide.

Cependant, je veux être en mesure de prendre n pour être grand, donc je préférerais un moyen plus rapide que cela. (Bien sûr, je pourrais changer cela en une boucle parfor ou le faire sur le GPU, ce n'est pas pratique pour ma situation actuelle, donc je préfère une version plus directe.)

J'ai regardé des choses comme dec2base, num2str, str2num, base2dec et ainsi de suite, mais sans chance. Toute suggestion serait très appréciée. En ce qui concerne la vitesse et l'espace, toute préallocation pour les entiers dans l'intervalle [0, q-1] ou similaire serait également bonne. Pour être clair, je cherche un algorithme qui fonctionne pour tout q et n, convertissant n'importe quel nombre dans l'intervalle [0, q^n - 1].

+0

'floor','^', et'/'sont déjà vectorisés. La boucle n'est pas nécessaire. – excaza

+0

Ah, bien sûr qu'ils le sont! Bien sûr, mon for-loop est des déchets et ne fournit pas ce que je veux du tout = P - laissez-moi juste changer cela ... –

Répondre

3

Vous pouvez utiliser dec2base et remplacer les caractères par des chiffres:

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

donne

>> result 
result = 
    1 11 

Cela fonctionne pour la base jusqu'à 36 seulement, en raison de dec2base limites.


Pour toute base (éventuellement au-dessus 36) vous devez faire la conversion manuellement. J'ai déjà écrit une fonction base2base pour faire cela (c'est essentiellement longue division). Le nombre doit être entré comme un vecteur de chiffres dans la base d'origine, donc vous devez d'abord dec2base(...,10). Par exemple:

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

donne

result = 
    3  2  5 

Ou si vous avez besoin de spécifier le nombre de chiffres:

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

EDIT (15 Août, 2017): Correction de deux bugs: gestion de l'entrée composée de tous les "zéros" (merci à @Sanchises pour avoir remarqué), et correctement l Eft-padding la sortie avec "zéros" si nécessaire.

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); 
+0

Désolé, je cherche à faire cela pour le générique 'q', donc ce n'est pas Ne travaille pas pour moi. Je vais clarifier cela dans la question. –

+0

Aussi, en utilisant votre algorithme avec 'x = 125' et' q = 16' (je suppose que vous vouliez dire ne fonctionne que pour 'q' jusqu'à 36, x peut être quelque chose) donne le résultat [7, -1], quand il devrait être [7, 13] ... –

+0

@SamT Désolé, il y avait une faute de frappe. Corrigé maintenant. Pour la base au-dessus de 36, vous devez le faire manuellement (c'est essentiellement la division longue). J'ai déjà écrit une fonction pour ça; laissez-moi le trouver –