Небольшая поправка к исходному ответу - при удалении также создается значительное количество повторов (так как отмена сама защищена повторами). Это видно из вывода autotrace:
SQL> delete from t1;
10918 rows deleted.
Elapsed: 00:00:00.58
Execution Plan
----------------------------------------------------------
0 DELETE STATEMENT Optimizer=FIRST_ROWS (Cost=43 Card=1)
1 0 DELETE OF 'T1'
2 1 TABLE ACCESS (FULL) OF 'T1' (TABLE) (Cost=43 Card=1)
Statistics
----------------------------------------------------------
30 recursive calls
12118 db block gets
213 consistent gets
142 physical reads
3975328 redo size
441 bytes sent via SQL*Net to client
537 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
10918 rows processed
В настройках вашего компилятора есть быстрые операции с плавающей запятой. Вам нужны точные операции с плавающей запятой. Дело в том, что log10 (8) / log10 (2) всегда равно 3 по математике. Но, возможно, ваш результат равен 2,99999, например. Это плохо. Необходимо добавить небольшую добавку, но не 0,5. Оно должно быть около 0,00001 или что-то в этом роде.
Почти верная формула:
int size = static_cast<int>((log10((double)num) / log10((double)base)) + 1.00000001);
Действительно верное решение
Вы должны проверить результат своей формулы. Сложность составляет O (log log n)
или O (log result)
!
int fast_power(int base, int s)
{
int res = 1;
while (s) {
if (s%2) {
res*=base;
s--;
} else {
s/=2;
base*=base;
}
}
return res;
}
int digits_size(int n, int base)
{
int s = int(log10(1.0*n)/log10(1.0*base)) + 1;
return fast_power(base, s) > n ? s : s+1;
}
Эта проверка лучше, чем проверка грубой силы с базовыми
умножениями.
Вот решение в bash:
% digits() { echo $1 $2 opq | dc | sed 's/ .//g;s/.//' | wc -c; }
% digits 10000000000 42
7
Я думаю, что единственный способ устранить ошибку округления без возникновения других ошибок - это использовать или реализовать целочисленные логарифмы.
Проблемы с округлением с плавающей запятой.
log10(216) / log10(6) = 2.9999999999999996
Но вы не можете добавить 0,5, как предлагается, потому что это не сработает для следующих
log10(1295) = log10(6) = 3.9995691928566091 // 5, 5, 5, 5
log10(1296) = log10(6) = 4.0 // 1, 0, 0, 0, 0
Возможно, использование функции log (значение, основание) позволит избежать этих ошибки округления.
Похоже, формула мне подходит:
Number 8 in base 2 : 1,0,0,0
Number of digits: 4
Formula returned: 3
log10(8) = 0.903089
log10(2) = 0.301029
Division => 3
+1 => 4
Так что это определенно просто ошибка округления.
Может быть полезно включить функцию округления (например, + 0,5) в ваш код где-нибудь: вполне вероятно, что деление дает (например) 2,99989787, к которому добавляется 1,0, что дает 3,99989787 и когда это преобразовывается в int , это дает 3.
static int numInBase(int num, int theBase)
{
if(num == 0) return 0;
if (num == theBase) return 1;
return 1 + numInBase(num/theBase,theBase);
}
Как отмечали другие, у вас есть ошибка округления, но предлагаемые решения просто перемещают опасную зону или делают ее меньше, они не устраняют ее. Если ваши числа являются целыми числами, вы можете проверить - с помощью целочисленной арифметики - что одна степень основания меньше или равна вашему числу, а следующая - над ним (первая степень - это число цифр). Но если вы используете арифметику с плавающей запятой где-либо в цепочке, вы будете уязвимы для ошибок (если ваша база не является степенью двойки, а может быть, даже тогда).
РЕДАКТИРОВАТЬ:
Вот грубое, но эффективное решение в целочисленных арифметика. Если ваши целочисленные классы могут содержать числа размером с основание * число, это даст правильный ответ.
size = 0, k = 1; while(k<=num) { k *= base; size += 1; }
Вам нужен потолок (= наименьшее целое число, не большее чем) log b (n + 1), а не то, что вы вычисляете прямо сейчас, floor (1 + log b (n)).
Вы можете попробовать:
int digits = (int) ceil( log((double)(n+1)) / log((double)base) );
Поскольку ваша формула верна (я только что попробовал), я думаю, что это ошибка округления в вашем делении, из-за которой число будет чуть меньше целого числа, которым оно должно быть. Поэтому, когда вы выполняете усечение до целого числа, вы теряете 1. Попробуйте добавить к окончательному значению дополнительные 0,5 (так что усечение будет фактически круглой операцией).
Подойдет любой из следующих вариантов:
>>> from math import *
>>> def digits(n, b=10):
... return int(1 + floor(log(n, b))) if n else 1
...
>>> def digits(n, b=10):
... return int(ceil(log(n + 1, b))) if n else 1
...
Первая версия описана на mathpath.org . Во второй версии + 1
необходимо, чтобы дать правильный ответ для любого числа n , которое является наименьшим числом с d цифрами в базе b. . То есть те числа, которые записываются 10 ... 0 в базе b . Обратите внимание, что ввод 0
должен рассматриваться как особый случай.
Десятичные примеры:
>>> digits(1)
1
>>> digits(9)
1
>>> digits(10)
2
>>> digits(99)
2
>>> digits(100)
3
Двоичный:
>>> digits(1, 2)
1
>>> digits(2, 2)
2
>>> digits(3, 2)
2
>>> digits(4, 2)
3
>>> digits(1027, 2)
11
Изменить : OP утверждает, что решение log
может не работать для больших входов. Я не знаю об этом, но если это так, следующий код не должен нарушаться, потому что он использует только целочисленную арифметику (на этот раз на C):
unsigned int
digits(unsigned long long n, unsigned long long b)
{
unsigned int d = 0;
while (d++, n /= b);
return d;
}
Этот код, вероятно, будет менее эффективным. И да , это было написано для максимальной точки неизвестности. Он просто использует наблюдение, что каждое число имеет по крайней мере одну цифру, и что каждое деление на b
, которое не дает 0
, подразумевает наличие дополнительной цифры. Более читаемая версия выглядит так:
unsigned int
digits(unsigned long long n, unsigned long long b)
{
unsigned int d = 1;
while (n /= b) {
d++;
}
return d;
}
Используя вашу формулу,
log(8)/log(2) + 1 = 4
проблема заключается в точности вычисления логарифма. Использование
ceil(log(n+1)/log(b))
должно решить эту проблему. Это не совсем то же самое, что
ceil(log(n)/log(b))
, потому что это дает ответ 3 для n = 8 b = 2, и не то же самое, что и
log(n+1)/log(b) + 1
, потому что это дает ответ 4 для n = 7 b = 2 (при вычислении до полной точности).
На самом деле я получаю любопытные результаты реализации и компиляции первой формы с помощью g ++:
double n = double(atoi(argv[1]));
double b = double(atoi(argv[2]));
int i = int(std::log(n)/std::log(b) + 1.0);
терпит неудачу (IE дает ответ 3), а
double v = std::log(n)/std::log(b) + 1.0;
int i = int(v);
успешен (дает ответ 4). Глядя на это еще раз, я думаю, что третья форма
ceil(log(n+0.5)/log(b))
была бы более стабильной, потому что она позволяет избежать «критического» случая, когда n (или n + 1 для второй формы) является целой степенью b (для целых значений n ).