J'ai vu cette question , et fait apparaître cette idée.
while (n % 3 == 0) {
n /= 3;
}
return n == 1;
Notez que 1 est le pouvoir zéro de trois.
Edit: Vous devez également vérifier le zéro avant la boucle, car celle-ci ne se terminera pas pour n = 0 (grâce à Bruno Rothgiesser).
Je me trouve légèrement pensant que si par "entier" vous voulez dire "entier signé 32 bits", alors (pseudocode)
return (n == 1)
or (n == 3)
or (n == 9)
...
or (n == 1162261467)
a une certaine belle simplicité (le dernier chiffre est 3 ^ 19, il n’ya donc pas un nombre absurde de cas). Même pour un entier non signé de 64 bits, il ne reste plus que 41 cas (merci à Alexandru de m'avoir signalé une erreur mentale). Et bien sûr, serait impossible pour l'arithmétique à précision arbitraire ...
Je suis surpris de cela. Tout le monde semble avoir raté l'algorithme le plus rapide de tous.
L'algorithme suivant est plus rapide en moyenne - et considérablement plus rapide dans certains cas - qu'une simple boucle while(n%3==0) n/=3;
:
bool IsPowerOfThree(uint n)
{
// Optimizing lines to handle the most common cases extremely quickly
if(n%3 != 0) return n==1;
if(n%9 != 0) return n==3;
// General algorithm - works for any uint
uint r;
n = Math.DivRem(n, 59049, out r); if(n!=0 && r!=0) return false;
n = Math.DivRem(n+r, 243, out r); if(n!=0 && r!=0) return false;
n = Math.DivRem(n+r, 27, out r); if(n!=0 && r!=0) return false;
n += r;
return n==1 || n==3 || n==9;
}
Les constantes numériques dans le code sont 3 ^ 10, 3 ^ 5 et 3 ^ 3.
Calculs de performance
Dans les CPU modernes, DivRem
est une instruction souvent unique qui prend un cycle. Sur d'autres, il se développe en une division suivie d'un mul et d'un add, ce qui prendrait plutôt trois cycles. Chaque étape de l’algorithme général semble longue, mais elle ne contient en réalité que: DivRem, cmp, cmove, cmp, cand, cjmp, add
. Il y a beaucoup de parallélisme disponible, donc sur un processeur superscalaire à deux voies typique, chaque étape sera probablement exécutée en environ 4 cycles d'horloge, ce qui donnera un temps d'exécution garanti dans le pire des cas d'environ 25 cycles d'horloge.
Si les valeurs d'entrée sont réparties uniformément sur la plage de UInt32
, voici les probabilités associées à cet algorithme:
Cet algorithme surpasse la boucle while(n%3==0) n/=3
simple, qui a les probabilités suivantes:
Ce qui est peut-être encore plus important, cet algorithme gère des puissances moyennes et grandes de trois (et leurs multiples) beaucoup plus efficacement: dans le pire des cas, l’algorithme simple consommera plus de 100 cycles de traitement car il effectuera une boucle 20 fois (41 fois pour 64 bits). L'algorithme que je présente ici ne prendra jamais plus de 25 cycles environ.
Extension à 64 bits
Étendre l'algorithme ci-dessus à 64 bits est trivial - il suffit d'ajouter une étape supplémentaire. Voici une version 64 bits de l'algorithme ci-dessus optimisé pour les processeurs sans division efficace de 64 bits:
bool IsPowerOfThree(ulong nL)
{
// General algorithm only
ulong rL;
nL = Math.DivRem(nL, 3486784401, out rL); if(nL!=0 && rL!=0) return false;
nL = Math.DivRem(nL+rL, 59049, out rL); if(nL!=0 && rL!=0) return false;
uint n = (uint)nL + (uint)rL;
n = Math.DivRem(n, 243, out r); if(n!=0 && r!=0) return false;
n = Math.DivRem(n+r, 27, out r); if(n!=0 && r!=0) return false;
n += r;
return n==1 || n==3 || n==9;
}
La nouvelle constante est 3 ^ 20. Les lignes d'optimisation sont omises en haut de la méthode car, en supposant que la division 64 bits soit lente, elles ralentiraient les choses.
Pourquoi cette technique fonctionne
Dites que je veux savoir si "100000000000000000" est une puissance de 10. Je pourrais suivre ces étapes:
Parce que j'ai commencé avec une puissance de 10, chaque fois que je me suis divisé par une puissance de 10, je me suis retrouvé avec un quotient nul ou un reste nul. Si j'avais commencé avec autre chose qu'une puissance de 10, j'aurais tôt ou tard fini avec un quotient non nul ou un reste.
Dans cet exemple, j'ai sélectionné des exposants de 10, 5 et 3 pour correspondre au code fourni précédemment et ajouté 2 juste pour le plaisir de le faire. D'autres exposants fonctionneraient également: il existe un algorithme simple pour sélectionner les exposants idéaux en fonction de votre valeur d'entrée maximale et de la puissance maximale autorisée de 10 dans la sortie, mais cette marge n'a pas assez de place pour la contenir.
NOTE: Vous avez peut-être pensé en base dix tout au long de cette explication, mais toute l'explication ci-dessus peut être lue et comprise de manière identique si vous réfléchissez en base 3, sauf que les exposants auraient été exprimés différemment ( au lieu de "10", "5", "3" et "2", je devrais dire "101", "12", "10" et "2").
si (log n)/(log 3) est intégrale, alors n est une puissance de 3.
Divisez récursivement par 3, vérifiez que le reste est égal à zéro et réappliquez-le au quotient.
Notez que 1 est une réponse valide car 3 à la puissance zéro est 1 est un cas Edge à se méfier.
Question très intéressante, j'aime bien le answer de starblue , Il s’agit d’une variante de son algorithme qui convergera un peu plus rapidement vers la solution:
private bool IsPow3(int n)
{
if (n == 0) return false;
while (n % 9 == 0)
{
n /= 9;
}
return (n == 1 || n == 3);
}
Ceci est un résumé de toutes les bonnes réponses ci-dessous, et les chiffres de performance peuvent être trouvés dans l'article de LeetCode .
Complexité temporelle O (log (n)), complexité spatiale O (1)
public boolean isPowerOfThree(int n) {
if (n < 1) {
return false;
}
while (n % 3 == 0) {
n /= 3;
}
return n == 1;
}
Convertissez l'entier en nombre de base 3 et vérifiez s'il est écrit sous la forme d'un 1, suivi de tous les 0. Il s'inspire de la solution pour vérifier si un nombre est égal à 2 en faisant n & (n - 1) == 0
Complexité temporelle: O(log(n)) en fonction du langage et du compilateur, complexité de l'espace: O (log (n))
public boolean isPowerOfThree(int n) {
return Integer.toString(n, 3).matches("^10*$");
}
Si n = 3^i
, alors i = log(n) / log(3)
, et vient donc à la solution
Complexité temporelle: en fonction de la langue et du compilateur, complexité de l'espace: O (1)
public boolean isPowerOfThree(int n) {
return (Math.log(n) / Math.log(3) + epsilon) % 1 <= 2 * epsilon;
}
Parce que 3^19 = 1162261467
est la plus grande puissance de 3 nombres tient dans un entier 32 bits, nous pouvons donc faire
Complexité temporelle: O (1), complexité spatiale: O (1)
public boolean isPowerOfThree(int n) {
return n > 0 && 1162261467 % n == 0;
}
L'idée est similaire à la n ° 4 mais utilisez un ensemble pour stocker toute la puissance possible de 3 nombres (de 3 ^ 0 à 3 ^ 19). Cela rend le code plus lisible.
Cette solution est spécifique à C++ 11 et utilise la méta-programmation de modèles de sorte que ce dernier remplacera l'appel isPowerOf3<Your Input>::cValue
par le résultat calculé.
Complexité temporelle: O (1), complexité spatiale: O (1)
template<int N>
struct isPowerOf3 {
static const bool cValue = (N % 3 == 0) && isPowerOf3<N / 3>::cValue;
};
template<>
struct isPowerOf3<0> {
static const bool cValue = false;
};
template<>
struct isPowerOf3<1> {
static const bool cValue = true;
};
int main() {
cout<<isPowerOf3<1162261467>::cValue;
return 0;
}
Entre deux puissances, il y a au plus une puissance de trois . Voici donc un test rapide:
Recherchez le logarithme binaire de n
en recherchant la position du bit 1
principal dans le nombre. C'est très rapide, car les processeurs modernes ont une instruction spéciale pour cela. (Sinon, vous pouvez le faire par petits tournants, voir Bit Twiddling Hacks ).
Recherchez la puissance potentielle de trois dans une table indexée par cette position et comparez-la à n
(s'il n'y a pas de puissance de trois, vous pouvez stocker n'importe quel nombre avec un logarithme binaire différent).
Si elles sont égales, oui, sinon non.
L'exécution dépend principalement du temps nécessaire pour accéder à l'entrée de la table. Si nous utilisons des entiers machine, la table est petite et probablement en cache (nous l'utilisons plusieurs millions de fois, sinon ce niveau d'optimisation n'aurait aucun sens).
Solution simple et à temps constant:
return n == power(3, round(log(n) / log(3)))
Pour les très grands nombres n
, vous pouvez utiliser l’astuce mathématique suivante pour accélérer le fonctionnement de
n % 3 == 0
ce qui est vraiment lent et très probablement le point d’étouffement de tout algorithme reposant sur la vérification répétée des restes. Vous devez comprendre l'arithmétique modulaire pour suivre ce que je fais, ce qui fait partie de la théorie des nombres élémentaire.
Soit x = Σ k une k 2 k être le nombre d'intérêt. On peut laisser la limite supérieure de la somme ∞ avec la compréhension qu’un k = 0 pour certains k> M. Alors
0 ≡ x ≡ Σ k une k 2 k Σ k une 2k 2 2k + un 2k + 1 2 2k + 1 Σ k 2 2k ( une 2k + un 2k + 1 2) Σ k une 2k + un 2k + 1 2 (mod 3)
depuis 22k 4 k ≡ 1k ≡ 1 (mod 3).
Soit une représentation binaire d’un nombre x avec 2n + 1 bits comme
x X1 X2 ... X2n + 1
où xk ∈ {0,1} vous pouvez regrouper des paires paires impaires
(X X1) (X2 X3) ... (X2n X2n + 1).
Soit q le nombre d'appariements de la forme (1 0) et r indique le nombre d'appariements de la forme (0 1). Ensuite, il découle de l'équation ci-dessus que 3 | x si et seulement si 3 | (q + 2r). De plus, vous pouvez montrer que 3 | (q + 2r) si et seulement si q et r ont le même reste lorsqu'ils sont divisés par 3.
Donc, un algorithme pour déterminer si un nombre est divisible par 3 pourrait être fait comme suit
q = 0, r = 0
for i in {0,1, .., n}
pair <- (x_{2i} x_{2i+1})
if pair == (1 0)
switch(q)
case 0:
q = 1;
break;
case 1:
q = 2;
break;
case 2:
q = 0;
break;
else if pair == (0 1)
switch(r)
case 0:
r = 1;
break;
case 1:
r = 2;
break;
case 2:
r = 0;
return q == r
Cet algorithme est plus efficace que l'utilisation de%.
--- Modifier plusieurs années plus tard ----
J'ai pris quelques minutes pour implémenter une version rudimentaire de ceci en python qui vérifie sa véracité pour tous les nombres jusqu'à 10 ^ 4. Je l’inclus ci-dessous pour référence. Évidemment, utiliser cette option implémenterait cela aussi près que possible du matériel. Cette technique de numérisation peut être étendue à n’importe quel nombre voulu en modifiant la dérivation. Je suppose également que la partie "balayage" de l'algorithme peut être reformulée dans une formulation récursive de type O(log n)
similaire à une FFT, mais je devrais y réfléchir.
#!/usr/bin/python
def bits2num(bits):
num = 0
for i,b in enumerate(bits):
num += int(b) << i
return num
def num2bits(num):
base = 0
bits = list()
while True:
op = 1 << base
if op > num:
break
bits.append(op&num !=0)
base += 1
return "".join(map(str,map(int,bits)))[::-1]
def div3(bits):
n = len(bits)
if n % 2 != 0:
bits = bits + '0'
n = len(bits)
assert n % 2 == 0
q = 0
r = 0
for i in range(n/2):
pair = bits[2*i:2*i+2]
if pair == '10':
if q == 0:
q = 1
Elif q == 1:
q = 2
Elif q == 2:
q = 0
Elif pair == '01':
if r == 0:
r = 1
Elif r == 1:
r = 2
Elif r == 2:
r = 0
else:
pass
return q == r
for i in range(10000):
truth = (i % 3) == 0
bits = num2bits(i)
check = div3(bits)
assert truth == check
Voici une belle et rapide implémentation de la méthode Ray Burns en C:
bool is_power_of_3(unsigned x) {
if (x > 0x0000ffff)
x *= 0xb0cd1d99; // multiplicative inverse of 59049
if (x > 0x000000ff)
x *= 0xd2b3183b; // multiplicative inverse of 243
return x <= 243 && ((x * 0x71c5) & 0x5145) == 0x5145;
}
Il utilise l'astuce multiplicative inverse pour diviser d'abord par 3 ^ 10, puis par 3 ^ 5. Enfin, il doit vérifier si le résultat est 1, 3, 9, 27, 81 ou 243, ce qui se fait par un simple hachage que j'ai trouvé par essais et erreurs.
Sur mon processeur (Intel Sandy Bridge), il est assez rapide, mais pas aussi rapide que la méthode de starblue qui utilise le logarithme binaire (implémenté matériellement dans ce processeur). Mais sur un CPU sans une telle instruction, ou lorsque des tables de recherche ne sont pas souhaitables, cela peut constituer une alternative.
Quelle est la taille de votre contribution? Avec O(log(N)) mémoire, vous pouvez faire plus rapidement, O (log (log (N)). Précalculez les puissances de 3 puis faites une recherche binaire sur les valeurs précalculées.
La solution la plus rapide est de tester si n > 0 && 3**19 % n == 0
comme indiqué dans une autre réponse ou un hachage parfait (ci-dessous). Je donne d’abord deux solutions basées sur la multiplication.
Je me demande pourquoi tout le monde a manqué cette multiplication est beaucoup plus rapide que la division:
for (int i=0, pow=1; i<=19, pow*=3; ++i) {
if (pow >= n) {
return pow == n;
}
}
return false;
Essayez tous les pouvoirs, arrêtez-vous quand il est devenu trop gros. Évitez les débordements, car 3**19 = 0x4546B3DB
est le plus puissant montage d’aluminium signé 32 bits.
La recherche binaire pourrait ressembler à
int pow = 1;
int next = pow * 6561; // 3**8
if (n >= next) pow = next;
next = pow * 81; // 3**4
if (n >= next) pow = next;
next = pow * 81; // 3**4; REPEATED
if (n >= next) pow = next;
next = pow * 9; // 3**2
if (n >= next) pow = next;
next = pow * 3; // 3**1
if (n >= next) pow = next;
return pow == next;
Une étape est répétée, de sorte que l'exposant maximum 19 = 8+4+4+2+1
puisse être exactement atteint.
Il y a 20 puissances de trois convenant à un int signé 32 bits, nous prenons donc une table de 32 éléments. Avec quelques expériences, j'ai trouvé la fonction de hachage parfaite
def hash(x):
return (x ^ (x>>1) ^ (x>>2)) & 31;
mapper chaque puissance à un indice distinct compris entre 0 et 31. Le reste est trivial:
// Create a table and fill it with some power of three.
table = [1 for i in range(32)]
// Fill the buckets.
for n in range(20): table[hash(3**n)] = 3**n;
Maintenant nous avons
table = [
1162261467, 1, 3, 729, 14348907, 1, 1, 1,
1, 1, 19683, 1, 2187, 81, 1594323, 9,
27, 43046721, 129140163, 1, 1, 531441, 243, 59049,
177147, 6561, 1, 4782969, 1, 1, 1, 387420489]
et peut tester très rapidement via
def isPowerOfThree(x):
return table[hash(x)] == x
Vous pouvez faire mieux qu'une division répétée, qui prend O(lg(X) * | division |). Essentiellement, vous effectuez une recherche binaire sur des puissances de 3. En réalité, nous effectuerons une recherche binaire sur N, où 3 ^ N = valeur d'entrée). Le réglage du chiffre binaire Pth de N correspond à la multiplication par 3 ^ (2 ^ P), et des valeurs de la forme 3 ^ (2 ^ P) peuvent être calculées par quadrature répétée.
Algorithme
Complexité:
O (lg (lg (X)) * | multiplication |) - Générer et itérer sur L prend lg (lg (X)) itérations, et la multiplication est l'opération la plus chère d'une itération.
Il est assez facile de répondre à votre question en définissant une fonction simple pour exécuter le contrôle pour vous. L'exemple d'implémentation présenté ci-dessous est écrit en Python mais ne devrait pas être difficile à réécrire dans d'autres langues si nécessaire. Contrairement à la dernière version de cette réponse, le code ci-dessous est beaucoup plus fiable.
Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import math
>>> def power_of(number, base):
return number == base ** round(math.log(number, base))
>>> base = 3
>>> for power in range(21):
number = base ** power
print(f'{number} is '
f'{"" if power_of(number, base) else "not "}'
f'a power of {base}.')
number += 1
print(f'{number} is '
f'{"" if power_of(number, base) else "not "}'
f'a power of {base}.')
print()
1 is a power of 3.
2 is not a power of 3.
3 is a power of 3.
4 is not a power of 3.
9 is a power of 3.
10 is not a power of 3.
27 is a power of 3.
28 is not a power of 3.
81 is a power of 3.
82 is not a power of 3.
243 is a power of 3.
244 is not a power of 3.
729 is a power of 3.
730 is not a power of 3.
2187 is a power of 3.
2188 is not a power of 3.
6561 is a power of 3.
6562 is not a power of 3.
19683 is a power of 3.
19684 is not a power of 3.
59049 is a power of 3.
59050 is not a power of 3.
177147 is a power of 3.
177148 is not a power of 3.
531441 is a power of 3.
531442 is not a power of 3.
1594323 is a power of 3.
1594324 is not a power of 3.
4782969 is a power of 3.
4782970 is not a power of 3.
14348907 is a power of 3.
14348908 is not a power of 3.
43046721 is a power of 3.
43046722 is not a power of 3.
129140163 is a power of 3.
129140164 is not a power of 3.
387420489 is a power of 3.
387420490 is not a power of 3.
1162261467 is a power of 3.
1162261468 is not a power of 3.
3486784401 is a power of 3.
3486784402 is not a power of 3.
>>>
NOTE: La dernière révision a rendu cette réponse presque identique à réponse de TMS .
Set basé sur la solution ...
DECLARE @LastExponent smallint, @SearchCase decimal(38,0)
SELECT
@LastExponent = 79, -- 38 for bigint
@SearchCase = 729
;WITH CTE AS
(
SELECT
POWER(CAST(3 AS decimal(38,0)), ROW_NUMBER() OVER (ORDER BY c1.object_id)) AS Result,
ROW_NUMBER() OVER (ORDER BY c1.object_id) AS Exponent
FROM
sys.columns c1, sys.columns c2
)
SELECT
Result, Exponent
FROM
CTE
WHERE
Exponent <= @LastExponent
AND
Result = @SearchCase
Avec SET STATISTICS TIME ON
, enregistrez la plus basse possible, 1 milliseconde.
Une autre approche consiste à générer un tableau sur le temps de compilation. La bonne chose est que vous pouvez étendre cela aux pouvoirs de 4, 5, 6, 7,
template<std::size_t... Is>
struct seq
{ };
template<std::size_t N, std::size_t... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...>
{ };
template<std::size_t... Is>
struct gen_seq<0, Is...> : seq<Is...>
{ };
template<std::size_t N>
struct PowersOfThreeTable
{
std::size_t indexes[N];
std::size_t values[N];
static constexpr std::size_t size = N;
};
template<typename LambdaType, std::size_t... Is>
constexpr PowersOfThreeTable<sizeof...(Is)>
generatePowersOfThreeTable(seq<Is...>, LambdaType evalFunc)
{
return { {Is...}, {evalFunc(Is)...} };
}
template<std::size_t N, typename LambdaType>
constexpr PowersOfThreeTable<N> generatePowersOfThreeTable(LambdaType evalFunc)
{
return generatePowersOfThreeTable(gen_seq<N>(), evalFunc);
}
template<std::size_t Base, std::size_t Exp>
struct Pow
{
static constexpr std::size_t val = Base * Pow<Base, Exp-1ULL>::val;
};
template<std::size_t Base>
struct Pow<Base, 0ULL>
{
static constexpr std::size_t val = 1ULL;
};
template<std::size_t Base>
struct Pow<Base, 1ULL>
{
static constexpr std::size_t val = Base;
};
constexpr std::size_t tableFiller(std::size_t val)
{
return Pow<3ULL, val>::val;
}
bool isPowerOfThree(std::size_t N)
{
static constexpr unsigned tableSize = 41; //choosen by fair dice roll
static constexpr PowersOfThreeTable<tableSize> table =
generatePowersOfThreeTable<tableSize>(tableFiller);
for(auto a : table.values)
if(a == N)
return true;
return false;
}
J'ai mesuré les temps (C #, Platform target x64) pour certaines solutions.
using System;
class Program
{
static void Main()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
for (uint n = ~0u; n > 0; n--) ;
Console.WriteLine(sw.Elapsed); // nada 1.1 s
sw.Restart();
for (uint n = ~0u; n > 0; n--) isPow3a(n);
Console.WriteLine(sw.Elapsed); // 3^20 17.3 s
sw.Restart();
for (uint n = ~0u; n > 0; n--) isPow3b(n);
Console.WriteLine(sw.Elapsed); // % / 10.6 s
Console.Read();
}
static bool isPow3a(uint n) // Elric
{
return n > 0 && 3486784401 % n == 0;
}
static bool isPow3b(uint n) // starblue
{
if (n > 0) while (n % 3 == 0) n /= 3;
return n == 1;
}
}
Une autre façon (de couper les cheveux en quatre).
using System;
class Program
{
static void Main()
{
Random Rand = new Random(0); uint[] r = new uint[512];
for (int i = 0; i < 512; i++)
r[i] = (uint)(Rand.Next(1 << 30)) << 2 | (uint)(Rand.Next(4));
var sw = System.Diagnostics.Stopwatch.StartNew();
for (int i = 1 << 23; i > 0; i--)
for (int j = 0; j < 512; j++) ;
Console.WriteLine(sw.Elapsed); // 0.3 s
sw.Restart();
for (int i = 1 << 23; i > 0; i--)
for (int j = 0; j < 512; j++) isPow3c(r[j]);
Console.WriteLine(sw.Elapsed); // 10.6 s
sw.Restart();
for (int i = 1 << 23; i > 0; i--)
for (int j = 0; j < 512; j++) isPow3b(r[j]);
Console.WriteLine(sw.Elapsed); // 9.0 s
Console.Read();
}
static bool isPow3c(uint n)
{ return (n & 1) > 0 && 3486784401 % n == 0; }
static bool isPow3b(uint n)
{ if (n > 0) while (n % 3 == 0) n /= 3; return n == 1; }
}