与えられた範囲の素数


10

最近、すべての素数(1〜100)を印刷するタスクが与えられました。私はそこに劇的に失敗しました。私のコード:

Create Procedure PrintPrimeNumbers
@startnum int,
@endnum int
AS 
BEGIN
Declare @a INT;
Declare @i INT = 1
(
Select a = @startnum / 2;
WHILE @i<@a
BEGIN
@startnum%(@a-@i)
i=i+1;
)
END

完成せずに終わってしまいましたが、データベース(SQL Server 2008 R2)でこのようなプログラムを実行することは可能でしょうか。

はいの場合、どのように終了する可能性があります。


2
:与えられた回答のいずれかから奪うが、これは私が話題に見た中で最高の品であることではないsqlblog.com/blogs/hugo_kornelis/archive/2006/09/23/...
エリック・ダーリン

目標は1〜100、または任意の範囲であり、1〜100は単なる例の範囲でしたか?
ソロモンRutzky 2016

私の質問では、それは1から100でした。私は一般的なアプローチを取得してから、具体的なアプローチを取得するのが良いでしょう。
ispostback 2016

回答:


11

これまでで最も迅速かつ最も簡単な方法は、印刷「すべての素数(1-100)は、」完全にAの中に素数が知られているという事実は、有限、不変の値のセット(「既知」と「有限」を採用することですもちろん、特定の範囲)。この小さな規模で、非常に長い間知られている値の束を計算するために毎回CPUを浪費し、格納するメモリをほとんど消費しないのはなぜですか?

SELECT tmp.[Prime]
FROM   (VALUES (2), (3), (5), (7), (11), (13),
        (17), (19), (23), (29), (31), (37), (41),
        (43), (47), (53), (59), (61), (67), (71),
        (73), (79), (83), (89), (97)) tmp(Prime)

もちろん、1から100までの素数を計算する必要がある場合は、次の方法がかなり効率的です。

;WITH base AS
(
    SELECT tmp.dummy, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS [num]
    FROM   (VALUES (0), (0), (0), (0), (0), (0), (0)) tmp(dummy)
), nums AS
(
    SELECT  (ROW_NUMBER() OVER (ORDER BY (SELECT 1)) * 2) + 1 AS [num]
    FROM        base b1
    CROSS JOIN  base b2
), divs AS
(
    SELECT  [num]
    FROM        base b3
    WHERE   b3.[num] > 4
    AND     b3.[num] % 2 <> 0
    AND     b3.[num] % 3 <> 0
)
SELECT  given.[num] AS [Prime]
FROM        (VALUES (2), (3)) given(num)
UNION ALL
SELECT  n.[num] AS [Prime]
FROM        nums n
WHERE   n.[num] % 3 <> 0
AND     NOT EXISTS (SELECT *
                    FROM divs d
                    WHERE d.[num] <> n.[num]
                    AND n.[num] % d.[num] = 0
                    );

とにかく偶数は素数ではないため、このクエリは奇数のみをテストします。また、1〜100の範囲に固有です。

ここで、(質問のサンプルコードに示されているものと同様の)ダイナミックレンジが必要な場合、以下は、まだかなり効率的な上記のクエリの適応です(1-100,000-9592の範囲を計算しました)エントリ-1秒未満):

DECLARE  @RangeStart INT = 1,
         @RangeEnd INT = 100000;
DECLARE  @HowMany INT = CEILING((@RangeEnd - @RangeStart + 1) / 2.0);

;WITH frst AS
(
    SELECT  tmp.thing1
    FROM        (VALUES (0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) tmp(thing1)
), scnd AS
(
    SELECT  0 AS [thing2]
    FROM        frst t1
    CROSS JOIN frst t2
    CROSS JOIN frst t3
), base AS
(
    SELECT  TOP( CONVERT( INT, CEILING(SQRT(@RangeEnd)) ) )
            ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS [num]
    FROM        scnd s1
    CROSS JOIN  scnd s2
), nums AS
(
    SELECT  TOP (@HowMany)
            (ROW_NUMBER() OVER (ORDER BY (SELECT 1)) * 2) + 
                (@RangeStart - 1 - (@RangeStart%2)) AS [num]
    FROM        base b1
    CROSS JOIN  base b2
), divs AS
(
    SELECT  [num]
    FROM        base b3
    WHERE   b3.[num] > 4
    AND     b3.[num] % 2 <> 0
    AND     b3.[num] % 3 <> 0
)
SELECT  given.[num] AS [Prime]
FROM        (VALUES (2), (3)) given(num)
WHERE   given.[num] >= @RangeStart
UNION ALL
SELECT  n.[num] AS [Prime]
FROM        nums n
WHERE   n.[num] BETWEEN 5 AND @RangeEnd
AND     n.[num] % 3 <> 0
AND     NOT EXISTS (SELECT *
                    FROM divs d
                    WHERE d.[num] <> n.[num]
                    AND n.[num] % d.[num] = 0
                    );

私のテスト(を使用SET STATISTICS TIME, IO ON;)は、このクエリが(これまでのところ)指定された他の2つの回答よりも優れていることを示しています。

範囲:1〜100

Query      Logical Reads       CPU Milliseconds    Elapsed Milliseconds
-------    ----------------    ----------------    -----------------

Solomon      0                 0                   0
Dan        396                 0                   0
Martin     394                 0                   1

範囲:1-10,000

Query      Logical Reads       CPU Milliseconds    Elapsed Milliseconds
-------    ----------------    ----------------    -----------------

Solomon        0                   47                170
Dan        77015                 2547               2559
Martin       n/a

範囲:1〜100,000

Query      Logical Reads       CPU Milliseconds    Elapsed Milliseconds
-------    ----------------    ----------------    -----------------

Solomon            0                 984                996
Dan        3,365,469             195,766            196,650
Martin           n/a

範囲:99,900-100,000

:このテストを実行するために、Danのコードのバグを修正する必要@startnumがありました- クエリに考慮されなかったため、常にで開始されました1Dividend.num <= @endnum行をに置き換えましたDividend.num BETWEEN @startnum AND @endnum

Query      Logical Reads       CPU Milliseconds    Elapsed Milliseconds
-------    ----------------    ----------------    -----------------

Solomon       0                   0                   1
Dan           0                 157                 158
Martin      n/a

範囲:1〜100,000(部分的な再テスト)

99,900〜100,000のテストに対するDanのクエリを修正した後、論理的な読み取りがリストされていないことに気付きました。したがって、この修正を適用したままこ​​の範囲を再テストしたところ、論理読み取りが再びなくなり、時間がわずかに改善されたことがわかりました(そして、はい、同じ数の行が返されました)。

Query      Logical Reads       CPU Milliseconds    Elapsed Milliseconds
-------    ----------------    ----------------    -----------------

Dan                0             179,594            180,096

の目的はROW_NUMBER() OVER (ORDER BY (SELECT 1))何ですか?ROW_NUMBER() OVER ()同等ではないでしょうか?
Lennart、2018

こんにちは@Lennart。使用しようとするOVER ()と、次のエラーが発生しますThe function 'ROW_NUMBER' must have an OVER clause with ORDER BY.。また、ではORDER BY、定数にすることはできません。したがって、サブクエリは定数を返します。
ソロモンルツキー

1
おかげで、SQLサーバーのこの制限に気づきませんでした。理にかなっている
レナルト

なぜ私が使用している場合DECLARE @RangeStart INT = 999900, @RangeEnd INT = 1000000;、それはしかし、すぐに私はセットとして働くDECLARE @RangeStart INT = 9999999900, @RangeEnd INT = 10000000000;ことは言いますかMsg 8115, Level 16, State 2, Line 1 Arithmetic overflow error converting expression to data type int. Msg 1014, Level 15, State 1, Line 5 A TOP or FETCH clause contains an invalid value.
Francesco Mantovani

1
@FrancescoMantovaniそのエラーは、値がの範囲外であることを示していますINTINT保持できる最大値は2,147,483,647で、開始値の9,999,999,900よりも小さくなっています。だけを実行しても、このエラーが発生しますDECLARE。変数のデータ型を次のように変更してみて、BIGINTその様子を確認できます。それをサポートするために、他の小さな変更が必要になる可能性があります。データ型の範囲については、int、bigint、smallint、tinyintをご覧ください。
ソロモンルツキー

7

2-100の範囲の素数(1は素数ではない)を返す単純だが非常に効率的ではない方法は次のようになります。

WITH Ten AS (SELECT * FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) V(N)),
     Hundred(N) AS (SELECT T1.N * 10 + T2.N + 1 FROM Ten T1, Ten T2)
SELECT H1.N
FROM   Hundred H1
WHERE  H1.N > 1
       AND NOT EXISTS(SELECT *
                      FROM   Hundred H2
                      WHERE  H2.N > 1
                             AND H1.N > H2.N
                             AND H1.N % H2.N = 0);

また、テーブルで2〜100の数値を具体化し、更新または削除を繰り返してSieve of Eratosthenesを実装することもできます。


4

データベースでそのようなプログラムを実行することは実行可能ですか?

はい、それは可能ですが、T-SQLがその仕事に適したツールだとは思いません。以下は、この問題に対するT-SQLのセットベースのアプローチの例です。

CREATE PROC dbo.PrintPrimeNumbers
    @startnum int,
    @endnum int
AS 
WITH 
     t4 AS (SELECT n FROM (VALUES(0),(0),(0),(0)) t(n))
    ,t256 AS (SELECT 0 AS n FROM t4 AS a CROSS JOIN t4 AS b CROSS JOIN t4 AS c CROSS JOIN t4 AS d)
    ,t16M AS (SELECT ROW_NUMBER() OVER (ORDER BY (a.n)) AS num FROM t256 AS a CROSS JOIN t256 AS b CROSS JOIN t256 AS c)
SELECT num
FROM t16M AS Dividend
WHERE
    Dividend.num <= @endnum
    AND NOT EXISTS(
        SELECT 1
        FROM t16M AS Divisor
        WHERE
            Divisor.num <= @endnum
            AND Divisor.num BETWEEN 2 AND SQRT(Dividend.num)
            AND Dividend.num % Divisor.num = 0
            AND Dividend.num <= @endnum
    );
GO
EXEC dbo.PrintPrimeNumbers 1, 100;
GO

0

以下のコードを書くとうまくいきます:

CREATE procedure sp_PrimeNumber(@number int)
as 
begin
declare @i int
declare @j int
declare @isPrime int
set @isPrime=1
set @i=2
set @j=2
while(@i<=@number)
begin
    while(@j<=@number)
    begin
        if((@i<>@j) and (@i%@j=0))
        begin
            set @isPrime=0
            break
        end
        else
        begin
            set @j=@j+1
        end
    end
    if(@isPrime=1)
    begin
        SELECT @i
    end
    set @isPrime=1
    set @i=@i+1
    set @j=2
end
end

上記では、素数を取得するストアドプロシージャを作成しました。

結果を知るために、ストアドプロシージャを実行します。

EXECUTE sp_PrimeNumber 100

0
DECLARE @UpperLimit INT, @LowerLimit INT

SET @UpperLimit = 500
SET @LowerLimit = 100

DECLARE @N INT, @P INT
DECLARE @Numbers TABLE (Number INT NULL)
DECLARE @Composite TABLE (Number INT NULL)

SET @P = @UpperLimit

IF (@LowerLimit > @UpperLimit OR @UpperLimit < 0 OR @LowerLimit < 0 )
    BEGIN
        PRINT 'Incorrect Range'
    END 
ELSE
    BEGIN
        WHILE @P > @LowerLimit
            BEGIN
                INSERT INTO @Numbers(Number) VALUES (@P)
                SET @N = 2
                WHILE @N <= @UpperLimit/2
                    BEGIN
                        IF ((@P%@N = 0 AND @P <> @N) OR (@P IN (0, 1)))
                            BEGIN
                                INSERT INTO @Composite(Number) VALUES (@P)
                                BREAK
                            END
                        SET @N = @N + 1
                    END
                SET @P = @P - 1
            END
        SELECT Number FROM @Numbers
        WHERE Number NOT IN (SELECT Number FROM @Composite)
        ORDER BY Number
        END
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.