計算5bit數的2種演算法

lt發表於2016-10-16

有人在itpub上出了這麼一道題

5bit數: 二進位制中包含5個1的數,例如11111,1011011,100011101...

要求如下:
輸入任意十進位制a,b 引數, a<b,b<1000000000
(1)求出a,b 之間的最小5bit數
(2)求出a,b 之間所有5bit數的和

效能要求:輸入50組測試用例,總消耗時間不能超過3s

1.直接法
由網友solomon_007 最先回答。但他的寫法需要Oracle12才能支援,所以,把它改寫成一個函式+一個sql,並用了2個繫結變數。

create or replace function fn_10to2(p_num number) return varchar2
is
   l_str varchar2(32767):=null;
   n1 number;
   n2 number;
begin
   n1 := p_num;
   loop
     n2 := mod(n1, 2);
     n1 := trunc(n1 / 2);
     l_str := to_char(n2) || l_str;
     exit when n1 = 0;
   end loop;
   return l_str;
end fn_10to2;
/

var a number
var b number
exec :a:=10000
exec :b:=200000

SQL> with t as (select level n,fn_10to2(level)v from dual connect by level <=:b)
  2  select min(n),sum(n) from t where regexp_count(v,'1')=5 and n between :a and :b;

              MIN(N)               SUM(N)
-------------------- --------------------
               10000            543234109

已用時間:  00: 00: 04.75

僅一個測試用例就不滿足時間要求了。 經過分析,以上演算法的問題出在太多的無效測試,假定範圍是0-31*2^5,最多有10個二進位制位,每個位都可能為0和1,因此剛好5位的數量大約佔1/10。另外9/10都是無效測試。
2.間接法
這種演算法先求長度為0-b的長度所有5bit數,然後計算那些落在a和b之間的數。這相當於在指定長度的位上求5個1的組合,而2進位制長度與數值是以2為底的對數關係。時間複雜度從O(n)降低為O(log n)。

SQL> with t as(select power(2,level-1)a from dual connect by level<=ceil(log(2,:b)))
  2  ,r as(select a.a+b.a+c.a+d.a+e.a v
  3  from t a,t b,t c,t d,t e
  4  where a.a<b.a and b.a<c.a and c.a<d.a and d.a<e.a)
  5  select min(v),sum(v) from r where v>=:a and v<=:b;

              MIN(V)               SUM(V)
-------------------- --------------------
               10000            543234109

已用時間:  00: 00: 00.07

SQL> exec :a:=0

PL/SQL 過程已成功完成。

已用時間:  00: 00: 00.03
SQL> exec :b:=1000000000

PL/SQL 過程已成功完成。

已用時間:  00: 00: 00.00
SQL> /

              MIN(V)               SUM(V)
-------------------- --------------------
                  31       25476202472250

已用時間:  00: 00: 00.85

可見,演算法2比演算法1快,而且b越大,越明顯。

相關文章