주어진 범위의 소수


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
주어진 답변을 빼앗지 말고이
Erik Darling

목표는 1-100 또는 모든 범위를 수행하고 1-100은 예제 범위입니까?
Solomon Rutzky

내 질문에, 그것은 1에서 100이었습니다. 나는 일반적인 접근 방식을 얻은 다음 특정 접근 방식을 얻는 것이 좋습니다.
ispostback

답변:


11

"모든 소수 (1-100)" 를 인쇄 하는 가장 빠르고 쉬운 방법 은 소수가 알려진, 유한 및 변하지 않는 값 세트 ( "알림"및 "유한")라는 사실을 완전히 수용하는 것입니다. 물론 특정 범위). 이 작은 규모에서 왜 오랫동안 알려진 많은 값을 계산하기 위해 매번 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;)는이 쿼리가 주어진 다른 두 가지 답변 (지금까지)보다 우수하다는 것을 보여줍니다.

사거리 : 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해야했습니다. 쿼리에 포함되지 않았으므로 항상에서 시작했습니다 1. Dividend.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

안녕하세요 @Lennart. 사용하려고 OVER ()하면 다음 오류가 발생 The function 'ROW_NUMBER' must have an OVER clause with ORDER BY.합니다. 그리고를 사용 ORDER BY하면 상수가 될 수 없으므로 하위 쿼리는 상수를 반환합니다.
Solomon Rutzky

1
감사합니다 .SQL 서버 에서이 제한을 알지 못했습니다. 이제 이해가됩니다
Lennart

왜 사용하는 경우 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이 오류는 귀하의 값이 범위를 벗어났다고 말합니다 INT. INT유지할 수있는 최대 값 은 2,147,483,647이며 시작 값인 9,999,999,900보다 작습니다. 를 실행하더라도 오류가 발생합니다 DECLARE. 변수 데이터 유형을 변경하여 BIGINT어떻게 진행되는지 확인할 수 있습니다 . 이를 지원하기 위해 다른 사소한 변경이 필요할 수 있습니다. 데이터 유형 범위는 int, bigint, smallint 및 tinyint 를 참조하십시오 .
Solomon Rutzky

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.