Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2006.01.15;
Скачать: [xml.tar.bz2];

Вниз

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

 
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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.53 MB
Время: 0.017 c
2-1135374392
JEK2
2005-12-24 00:46
2006.01.15
картинку в RichEdit


2-1135283527
Sysanin
2005-12-22 23:32
2006.01.15
Tregistry


4-1131450963
Uran
2005-11-08 14:56
2006.01.15
Журнал событий Windows 2003


14-1133958616
Mike Kouzmine
2005-12-07 15:30
2006.01.15
Слышали новость? Волгу снимают с производства!


2-1135667095
TStas
2005-12-27 10:04
2006.01.15
Как в RunTime добавлять/удалять страницы у PageControl?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский