Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2006.01.15;
Скачать: CL | DM;

Вниз

Помогите решить плиз...   Найти похожие ветки 

 
uzver.exe   (2005-12-14 11:49) [0]

Даны числа А, В, К (А,В - от 0 до 10^9 и К от 0 до 30)
Надо найти кол-во натур чисел между А и В
таких, что кол-во единиц в записи этих чисел равно К

Сразу приходит на ум решение в лоб - то есть перебором

var
a,b,i:longint;
k:byte;
n:byte;{number of "ones"}

function count_one(inp:longint):byte;
var
cnt:byte;
begin
cnt:=0;
 while (inp<>0) do begin
   inp:=(inp-1) and inp;
   cnt:=cnt+1;
 end;
 count_one:=cnt;
end;

n:=0;
readln(a);
readln(b);
readln(k);
for i:=a to b do begin
  if count_one(i)=k then begin
   n:=n+1;
  end;
end;
writeln("Result is: ",n);

Код работает приемлемо при В-А<1000000 Помогите оптимизировать его...


 
evvcom ©   (2005-12-14 12:17) [1]


> Помогите оптимизировать его

Да такое решение не оптимизируешь.
Мне первое, что приходит на ум, это решить эту задачу математически, а компьютер (программу) потом использовать только для того, чтобы посчитать результат полученной функции. Есть такая область в математике, комбинаторика называется, вот ее тебе и надо здесь применить.


 
Digitman ©   (2005-12-14 12:37) [2]


> в записи этих чисел


в задании следует явно уточнять систему (десятичная, двоичная, хренеричная и т.д.) ... несмотря на приведенный пример собственного кода ..


> кол-во единиц


каких "единиц"-то ?

десятеричных ? двоичных ? N-чных ?


 
Digitman ©   (2005-12-14 12:47) [3]

function GetDecimalOneCount(Value: dword): Integer;
begin
 Result :=0;
 while (Value > 0) do
   begin
     if Value mod 10 = 1 then
       Inc(Result);
     Value := Value div 10;
   end;
end;


 
uzver.exe   (2005-12-14 14:26) [4]

Пардон, не уточнил.
Даны числа в Десятичной системе А и В(1&#8804;A&#8804;B&#8804;10^9)
Надо найти кол-во чисел между А и В(A&#8804;Z&#8804;B)
таких, что кол-во единиц в ДВОИЧНОЙ записи этих
чисел было равно некому числу К, заданному в условии(1&#8804;K&#8804;30)

Один из вариантов - свести задачу к нахождению всех
вариантов построения числа z из k слогаемых, каждый из которых
равен двойке в какой либо степени(причём каждый раз новой)

еще пытался воспользоваться замечательным свойством ряда
чисел, вырыжающих кол-во единиц в дв. записи ряда натуральных чисел:
0  1  1  2  1  2  2  3  1  2  2  3  2  3  3  4

 0
 0{0+1}
 01{0+1}{1+1}
 0112{0+1}{1+1}{1+1}{2+1}
 01121223{0+1}{1+1}{1+1}{2+1}{1+1}{2+1}{2+1}{3+1}

таким образом:
 0
 01
 0112
 01121223
 0112122312232334

но пока я зашел в тупик


 
Digitman ©   (2005-12-14 14:33) [5]

function GetBinaryOneCount(Value: dword): Integer;
begin
Result :=0;
while (Value > 0) do
  begin
    if Value mod 2 = 1 then
      Inc(Result);
    Value := Value div 2;
  end;
end;

function GetOctalOneCount(Value: dword): Integer;
begin
Result :=0;
while (Value > 0) do
  begin
    if Value mod 8 = 1 then
      Inc(Result);
    Value := Value div 8;
  end;
end;

function GetHexadecimalOneCount(Value: dword): Integer;
begin
Result :=0;
while (Value > 0) do
  begin
    if Value mod 16 = 1 then
      Inc(Result);
    Value := Value div 16;
  end;
end;

...

function GetBase/B>OneCount(Base: Integer; Value: dword): Integer;
begin
Result :=0;
while (Value > 0) do
  begin
    if Value mod Base = 1 then
      Inc(Result);
    Value := Value div Base;
  end;
end;

...


 
evvcom ©   (2005-12-14 14:44) [6]

ниче не понял из твоих уточнений. Смотри комбинаторику. Допустим (на пальцах) у тебя А=0, В = 99, К=1. Тебя устраивают 01, 10, 12..19, 21, 31, ..., 91: итого 18 чисел. Теперь математически. У тебя два знакоместа, на которых может размещаться всего одна единица, т.е. надо найти количество сочетаний 1 из 2 (так вроде это произносится, давно было). Получаешь ответ 2. Теперь на остальных местах может располагаться любая цифра кроме 1, т.е. 9 цифр. Итого 2*9=18.


 
MBo ©   (2005-12-14 14:53) [7]


procedure TForm1.Button3Click(Sender: TObject);
var
 NN, KK, A, B, BB, Cnt: Integer;
 Stop: Boolean;

 procedure Colex(n, k: Integer; J: DWord);
 begin
   if J > B then
     Exit;
   if n = 0 then begin
     if J >= A then
       Inc(Cnt)
   end
   else begin
     if k < n then begin
       J := J and not (1 shl (n - 1));
       Colex(n - 1, k, J);
     end;
     if k > 0 then
       Colex(n - 1, k - 1, J or (1 shl (n - 1)));
   end;
 end;

begin
 KK := 10;
 Cnt := 0;
 A := 0;
 B := 1048575;
 BB := B;
 NN := 0;
 while BB > 0 do begin
   Inc(NN);
   BB := BB shr 1;
 end;
 if NN >= KK then
   Colex(NN, KK, 0);
 Caption := Format("От %d до %d  - %d чисел, в дв. записи которых %d ед.",
   [A, B, Cnt, KK]);
end;



 
uzver.exe   (2005-12-14 15:04) [8]

Спасибо, Digitman © за такой развёрнутый ответ!
Но проблема моего кода не в функции Подсчета кол-ва, единиц
а в том, что по сути дела я перебираю все числа между А и В
и проверяю их:
подходят(if (GetBinaryOneCount(i)=k) then ...) - увеличиваю счетчик чисел,
а нет - то не увеличиваю
но если А=0 а В=1 000 000 000 то на выполнение уйдет слишком много времени(для ускорения я воообще отказался от дополнительной ф-и
и делаю подсчет единиц и проверку в теле одного цикла)
Может на ассемблере вставку сделать? хотя врятли это поможет - как сказал evvcom - я не там копаю.


 
uzver.exe   (2005-12-14 15:15) [9]

Спасибо большое MBo © - Все понял!


 
MBo ©   (2005-12-14 15:34) [10]

>uzver.exe  
Если получится сделать нерекурсивный алгоритм генерации следующей в колексикографическом порядке комбинации - будет работать быстрее.


 
uzver.exe   (2005-12-14 15:34) [11]

И всётаки есть ещё более изящное решение
код MBo © при
А = 1
В = 1 000 000 000
К = 20
Считает слишком долго :(


 
MBo ©   (2005-12-14 15:57) [12]

>Считает слишком долго :(
Мне проще было придумать рекурсивный алгоритм - но рекурсия может притормаживать.

Для лексикограф. порядка есть нерекурсивный алгоритм. Попробуй.


 
uzver.exe   (2005-12-14 16:39) [13]

А нет ли какой-то математической зависимости кол-ва чисел z от A, B, K?


 
MBo ©   (2005-12-14 16:55) [14]

Для A=0 и B=2^N-1 нужных чисел будет C(n,k), для произвольной границы - сложнее.


 
MBo ©   (2005-12-14 17:26) [15]

Кажется, придумал. Сейчас попробую оформить идею.


 
uzver.exe   (2005-12-14 18:02) [16]

Для К=1 искомые Z будут степени двойки:
1 2 4 8 16 32 64 128 и т.д.
Для К=2 искомые Z будут:
3 5 6 9 10 12 17 18 20 24 33 34 36 40 48 65 66 68 72 80 96
Нет ли закономерности в этой последовательности
Если есть, такая ф-я GetZ(position:int):int; то можно будет строить
эту последовательность для произвольного диапазона и К

Что касается поиска путём перестановок, то я представляю себе это так:
Находим Максимальную степень двойки, которая меньше В
Все степени от 0 до Макс становятся алфавитом для размещения в К
позициях, для каждого варианта делаем проверку,
попадает ли Z диапазон A-B - тогда кол-во размещений равно Max!/(Max-k)!
И для больших К придется делать слишком, много проверок z


 
uzver.exe   (2005-12-14 19:36) [17]

Неужели ни у кого нет никаких идей?


 
MBo ©   (2005-12-14 22:27) [18]

>Неужели ни у кого нет никаких идей?
Есть, есть ;)
Практически мгновенно вычислится.
За логарифмическое время для A и B можно посчитать количество подходящих чисел, не превосходящих их, и получить разницу.

Идем от старшего бита к младшему. Если i-й бит установлен, добавляем к счетчику C(i,k), декрементируем k.
При этом следим за знаком k и еще за несколькими условиями.


 
uzver.exe   (2005-12-15 00:36) [19]

На словах я не совсем понял так что-ли?
Что за ф-я С(i,k) <=> mod?
Чем искать на произвольном диапазоне -
находим на 0-А и 0-В и вычитаем?

function eval(s,b,k:dword):dword;
var k1, k2, c1, c2, i:dword;
begin

k1:=k; //Для первого числа
for i:=31 to 0 do begin //От старшего к младшему
 if (a and 2^i = 2^i) then  begin //Если вернула True то на i-ом месте 1
       c1:=c1+i mod k;//Что за ф-я такая - C
       k1:=k1-1;     //Декремент К
 end;
end;

k2:=k; //Для второго числа
for i:=31 to 0 do begin
 if (b and 2^i = 2^i) then  begin
       c2:=c2+i mod k;//????
       k2:=k2-1;     //Декремент К
 end;
end;

result:=c2-c1;//Получаем разницу
end;


 
MBo ©   (2005-12-15 06:22) [20]

>Что за ф-я С(i,k)
Количество сочетаний из i по к


 
evvcom ©   (2005-12-15 09:11) [21]


> Что за ф-я С(i,k)

Я же писал, читай комбинаторику. C(i, k) = i! / (k! * (i-k)!)


 
uzver.exe   (2005-12-15 13:47) [22]

Вот ф-я с - вроде работает, а вот остальное - нет
как следить за знаком К , - если к < 0 то выходить из цикла или что?

> При этом следим за знаком k и еще за несколькими условиями.

и за какими ещё условиями следить то



function factdivdiv( n, k1, k2 :LONGLONG):LONGLONG;
var
t,t2,i:LONGLONG;
begin
  // computes (n! / k1! k2!) for combinations
  // assure k1 >= k2
  if (k1 < k2) then begin
   i := k1;
   k1 := k2;
   k2 := i;
  end;

  if (k1 > n) then t := 0 else begin
     // accumulate the factors for k2 factorial
     t:=1;
     while (k2 > 1) do  begin
      t := t * k2;
      k2:=k2-1;
     end;
     // accumulate the factors from n downto k1
     t2:=1;
     while (n > k1) do  begin
      t2 := t2 * n;
      n:=n-1;
     end;
     result := t2 div t;
  end;
end;

function C( n, r:LONGLONG):LONGLONG;
begin
  // combinations of n things taken r at a time, order not impt.
  //    Comb( n, 0 ) = 1, and Comb( n, n ) = 1
  //    Comb( n, r ) = 0 if r > n  or r < 0
  if ((r = 0) or (r = n)) then  result:=1 else begin
     if ((r > n) or (r < 0)) then
       result:=0  else  result:= factdivdiv( n, r, n-r );
   end;
end;

function Sqr2(n:byte):LONGLONG; //Возвращает n-ю степень двойки
var
i:integer;
begin
result:=1 ;
if n=0 then Exit;
for i:=0 to n do result:=result*2;
end;

function eval(a,b,k:LONGLONG):LONGLONG;
var k1, k2, c1, c2:LONGLONG;
   i:byte;
begin

k1:=k; //Для первого числа
for i:=31 to 0 do begin //От старшего к младшему
if (a and Sqr2(i) <> 0) then  begin //Если вернула True то на i-ом месте 1
      c1:=c1+c(i,k);
      k1:=k1-1;     //Декремент К
end;
end;

k2:=k; //Для второго числа
for i:=31 to 0 do begin
if (b and Sqr2(i) <> 0) then  begin
      c2:=c2+c(i,k);
      k2:=k2-1;     //Декремент К
end;
end;

result:=c2-c1;//Получаем разницу
end;



 
pl   (2005-12-15 15:21) [23]

Я бы вместо
for i:=31 to 0 do begin //От старшего к младшему
if (a and Sqr2(i) <> 0) then  begin //Если вернула True то на i-ом месте 1
     c1:=c1+c(i,k);
     k1:=k1-1;     //Декремент К
end

написал бы

for i:=63 to 0 do begin
a:=(l shr 1) or (l shl 63);
if (a mod 2 = 1) then  begin
     c1:=c1+c(i,k);
     dec(k1);
end;
end;

и функцию Sqr2 выкинул нафиг


 
uzver.exe   (2005-12-15 17:03) [24]

Вот изрядно подкорректированный код, но он всё равно не фурычит:
Покажите как правильно плиз, а то у меня уже крыша едет:

function eval(a,b:DWORD;k:integer):integer;
var k1, k2, c1, c2:integer;
   i:integer;
begin
c1:=0;
c2:=0;
k1:=k; //Для первого числа
i:=31;
while (i>=0) do begin //От старшего к младшему
a:=(a shl 1) or (a shr 31);
if ((a and 1)=1) then  begin //Если вернула True то на i-ом месте 1
      c1:=c1+c(i,k1);
      k1:=k1-1;     //Декремент К
      if k1<0 then  Exit;
end;
i:=i-1;
end;

k2:=k; //Для второго числа
i:=31;
while (i>=0) do begin
b:=(b shl 1) or (b shr 31);
if ((b and 1)=1) then  begin
      c2:=c2+c(i,k2);
      k2:=k2-1;     //Декремент К
      if k2<0 then  Exit;
end;
i:=i-1;
end;

result:=c2-c1;//Получаем разницу
end;



Почемуто немного "мажет", то есть почта дает правильный ответ, но
на 1 меньше, пытался C2:=1, но когда кол-во z вообще ноль,
(a=1,b=1,k=5) то облом
Обьясните где тут недочёт?


 
uzver.exe   (2005-12-15 19:32) [25]

Ау! Люди! Не молчите!


 
uzver.exe   (2005-12-15 23:28) [26]

Есть здесь кто нибудь?


 
uzver.exe   (2005-12-15 23:28) [27]

Есть здесь кто нибудь?


 
uzver.exe   (2005-12-25 21:53) [28]

Люди, уже неделя прошла! Помогите!



Страницы: 1 вся ветка

Текущий архив: 2006.01.15;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.034 c
3-1132298547
syte_ser78
2005-11-18 10:22
2006.01.15
доступ к результату запроса


8-1123660039
grant
2005-08-10 11:47
2006.01.15
Подскажите, если кто знает. Как писать захвать изображение с каме


5-1121207868
Brack
2005-07-13 02:37
2006.01.15
Как применить общее свойство к N обьектам?


6-1127764469
nevalex
2005-09-26 23:54
2006.01.15
поиск компьютеров в сети


2-1135602857
Fedddor
2005-12-26 16:14
2006.01.15
Подскажите плиз функцию определения дня недели по дате!