ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 18. 소수 찾기
    코딩 테스트/Level 2 2020. 8. 1. 23:59
    반응형

    https://programmers.co.kr/learn/courses/30/lessons/42839

     

    코딩테스트 연습 - 소수 찾기

    한자리 숫자가 적힌 종이 조각이 흩어져있습니다. 흩어진 종이 조각을 붙여 소수를 몇 개 만들 수 있는지 알아내려 합니다. 각 종이 조각에 적힌 숫자가 적힌 문자열 numbers가 주어졌을 때, 종이 �

    programmers.co.kr

    파이썬

    from itertools import permutations
    
    def solution(numbers):
        numbers_set = {int("".join(i)) for r in range(1, len(numbers)+1) for i in permutations(numbers, r)}
        max_num = max(numbers_set)
        a = [False, False] + [True] * (max_num - 1)
        primes = set()
        for i in range(2, max_num + 1):
            if a[i]:
                primes.add(i)
                for j in range(2 * i, max_num + 1, i):
                    a[j] = False
        return len(primes & numbers_set)
    테스트 1 〉	통과 (0.61ms, 10.9MB)
    테스트 2 〉	통과 (112.58ms, 20.8MB)
    테스트 3 〉	통과 (0.07ms, 10.9MB)
    테스트 4 〉	통과 (53.99ms, 16.6MB)
    테스트 5 〉	통과 (837.15ms, 61.6MB)
    테스트 6 〉	통과 (0.07ms, 10.8MB)
    테스트 7 〉	통과 (0.61ms, 11MB)
    테스트 8 〉	통과 (1416.37ms, 95.3MB)
    테스트 9 〉	통과 (0.11ms, 10.9MB)
    테스트 10 〉	통과 (169.07ms, 25.7MB)
    테스트 11 〉	통과 (16.21ms, 12.5MB)
    테스트 12 〉	통과 (9.39ms, 12MB)

    리스트 컴프리헨션 내의 2중 for 문은 실전에선 애매합니다. 
    다시 보면 좀 혼란스럽습니다... 

    리턴 문에서 교집합 연산을 제거하고, 
    한 줄을 더 늘려 속도가 올렸습니다. 

    from itertools import permutations
    
    def solution(numbers):
        answer = 0
        numbers_set = {int("".join(i)) for r in range(1, len(numbers)+1) for i in permutations(numbers, r)}
        max_num = max(numbers_set)
        a = [False, False] + [True] * (max_num - 1)
        for i in range(2, max_num + 1):
            if a[i]:
                if i in numbers_set:
                    answer += 1
                for j in range(2 * i, max_num + 1, i):
                    a[j] = False
        return answer
    테스트 1 〉	통과 (0.55ms, 10.9MB)
    테스트 2 〉	통과 (113.78ms, 20.8MB)
    테스트 3 〉	통과 (0.07ms, 10.9MB)
    테스트 4 〉	통과 (50.82ms, 15.4MB)
    테스트 5 〉	통과 (721.26ms, 61.5MB)
    테스트 6 〉	통과 (0.06ms, 10.9MB)
    테스트 7 〉	통과 (0.59ms, 10.9MB)
    테스트 8 〉	통과 (1079.97ms, 95.5MB)
    테스트 9 〉	통과 (0.12ms, 10.9MB)
    테스트 10 〉	통과 (177.72ms, 25.7MB)
    테스트 11 〉	통과 (15.29ms, 12.2MB)
    테스트 12 〉	통과 (8.19ms, 11.4MB)

    집합 자료형과 에라스토테네스의 채로 나름 최적화했지만..
    다른 분의 코드를 보니... (와우~! 이 건 뭐지?)
    코드량은 절반, 속도는 최대 2배가량 빠름~! ㄷㄷㄷㄷ
    집합에서 요소를 바로 제거해주었네요..
    왜 이 생각을 못했을까..  

    from itertools import permutations
    
    
    def solution(numbers):
        numbers_set = {int("".join(i)) for r in range(1, len(numbers)+1) for i in permutations(numbers, r)}
        numbers_set -= {0, 1}
        for i in range(2, int(max(numbers_set) **0.5) + 1):
            numbers_set -= set(range(2 * i, max(numbers_set) + 1, i))
        return len(numbers_set)
    테스트 1 〉	통과 (0.53ms, 11.1MB)
    테스트 2 〉	통과 (73.84ms, 44.8MB)
    테스트 3 〉	통과 (0.09ms, 11MB)
    테스트 4 〉	통과 (49.31ms, 20.5MB)
    테스트 5 〉	통과 (191.92ms, 146MB)
    테스트 6 〉	통과 (0.07ms, 10.9MB)
    테스트 7 〉	통과 (0.64ms, 11MB)
    테스트 8 〉	통과 (528.21ms, 281MB)
    테스트 9 〉	통과 (0.10ms, 10.9MB)
    테스트 10 〉	통과 (338.50ms, 51.8MB)
    테스트 11 〉	통과 (26.02ms, 14.5MB)
    테스트 12 〉	통과 (4.88ms, 13.9MB)

    GO

    import (
    	"sort"
    	"strconv"
    	"strings"
    )
    
    func count(nums string) map[rune]int {
    	numCount := make(map[rune]int)
    	for _, v := range nums {
    		numCount[v]++
    	}
    	return numCount
    }
    
    func solution(numbers string) (rtn int) {
    
    	// numbers에서 사용된 숫자의 개수 카운트
    	numCount := count(numbers)
    
    	// 종이에서 나올 수 있는 최대값을 구함.
    	numStr := strings.Split(numbers, "")
    	sort.Sort(sort.Reverse(sort.StringSlice(numStr)))
    	max, _ := strconv.Atoi(strings.Join(numStr, ""))
    
    	// 에라토스테네스의 체
    	// true로 초기화하기 번거로우니, 반대로 소수아님 == true 으로함)
    	notPrimes := make([]bool, max+1)
    	notPrimes[0], notPrimes[1] = true, true
    	for i := 2; i <= max; i++ {
    		if notPrimes[i] { // 소수가 아닌 경우
    			continue
    		}
    		for j := i * 2; j <= max; j += i {
    			notPrimes[j] = true
    		}
    		success := true // 성공 여부를 확인할 체커
    
    		// 소수에서 사용된 숫자의 개수 카운트
    		primeCount := count(strconv.Itoa(i))
    
    		// 소수와 numbers의 사용 숫자 개수 비교
    		for k, primeV := range primeCount {
    			if numV, ok := numCount[k]; !ok || primeV > numV {
    				success = false
    				break
    			}
    		}
    		if success == true {
    			rtn++
    		}
    	}
    	return
    }
    테스트 1 〉	통과 (0.30ms, 7.23MB)
    테스트 2 〉	통과 (29.96ms, 11.2MB)
    테스트 3 〉	통과 (0.04ms, 7.16MB)
    테스트 4 〉	통과 (15.79ms, 10.9MB)
    테스트 5 〉	통과 (134.97ms, 15.6MB)
    테스트 6 〉	통과 (0.05ms, 7.25MB)
    테스트 7 〉	통과 (0.31ms, 7.32MB)
    테스트 8 〉	통과 (213.74ms, 19.9MB)
    테스트 9 〉	통과 (0.09ms, 7.15MB)
    테스트 10 〉	통과 (41.91ms, 11.1MB)
    테스트 11 〉	통과 (5.71ms, 9.4MB)
    테스트 12 〉	통과 (3.63ms, 8.54MB)

    느린 느낌이라.. 조금이나마 속도를 올리기 위해 몇 줄 추가해 보았습니다.

    import (
    	"sort"
    	"strconv"
    	"strings"
    )
    
    func solution(numbers string) (rtn int) {
    
    	numCount := make(map[rune]int)
    	for _, v := range numbers {
    		numCount[v]++
    	}
    
    	numStr := strings.Split(numbers, "")
    	sort.Sort(sort.Reverse(sort.StringSlice(numStr)))
    	max, _ := strconv.Atoi(strings.Join(numStr, ""))
    
    	notPrimes := make([]bool, max+1)
    	notPrimes[0], notPrimes[1] = true, true
    	for i := 2; i <= max; i++ {
    		if !notPrimes[i] {
    			for j := i * 2; j <= max; j += i {
    				notPrimes[j] = true
    			}
    		}
    	}
    
    	for i, v := range notPrimes {
    		if v { // 소수가 아닌 경우
    			continue
    		}
    		success := true
    		primeCount := make(map[rune]int)
    		for _, primeNum := range strconv.Itoa(i) {
    			if _, ok := numCount[primeNum]; !ok {
    				success = false
    				break
    			}
    			primeCount[primeNum]++
    		}
    		if success == false {
    			continue
    		}
    		for primeNum, v := range primeCount {
    			if v > numCount[primeNum] {
    				success = false
    				break
    			}
    		}
    		if success == true {
    			rtn++
    		}
    	}
    	return
    }
    테스트 1 〉	통과 (0.11ms, 11MB)
    테스트 2 〉	통과 (11.53ms, 10.4MB)
    테스트 3 〉	통과 (0.03ms, 9.11MB)
    테스트 4 〉	통과 (4.19ms, 9.59MB)
    테스트 5 〉	통과 (44.55ms, 16.3MB)
    테스트 6 〉	통과 (0.04ms, 9.33MB)
    테스트 7 〉	통과 (0.12ms, 9.04MB)
    테스트 8 〉	통과 (69.71ms, 19.5MB)
    테스트 9 〉	통과 (0.07ms, 11.1MB)
    테스트 10 〉	통과 (15.26ms, 11.1MB)
    테스트 11 〉	통과 (1.59ms, 9.32MB)
    테스트 12 〉	통과 (1.03ms, 9.14MB)

    코드가 장황해졌지만 속도는 약 3배 정도 빨라졌습니다.
    좀 더 깔끔하게 코드를 쓰고 싶지만 제 실력의 한계군요.
     

    순열 없이 문제를 풀어보고 싶어서...
    고 언어의 맵을 이용하면 될 것 같아 시도해 보았습니다만...
    결과는... 그냥 그렇습니다. 

    C#

    위 GO 코드를 C#에서 재작성해보았는데요..

    using System.Collections.Generic;
    using System.Linq;
    
    public class Solution
    {
        public int solution(string numbers)
        {
            int answer = 0;
            int max = int.Parse(string.Join("", numbers.OrderByDescending(c => c).ToArray())); 
            bool[] notPrimes = new bool[max + 1]; 
            for (int i = 2; i <= max; i++)
            {
                if (notPrimes[i] == true)
                    continue;
                for (int j = i * 2; j <= max; j += i)
                    notPrimes[j] = true;
            }
            Dictionary<char, int> numCount = new Dictionary<char, int>();
            foreach (char c in numbers)
            {
                if (!numCount.ContainsKey(c))
                    numCount.Add(c, 1);
                else
                    numCount[c]++;
            }
            for (int i = 2; i < notPrimes.Length; i++)
            {
                if (notPrimes[i] == true)
                    continue;
                Dictionary<char, int> temp = new Dictionary<char, int>();
                bool success = true;
                foreach (char c in i.ToString())
                {
                    if (!numCount.ContainsKey(c))
                    {
                        success = false;
                        break;
                    }
                    if (!temp.ContainsKey(c))
                        temp.Add(c, 1);
                    else
                        temp[c]++;
                }
                if (success == false)
                    continue;
                foreach (char c in temp.Keys)
                    if (temp[c] > numCount[c])
                    {
                        success = false;
                        break;
                    }
                if (success == true)
                    answer++;
            }
            return answer;
        }
    }
    테스트 1 〉	통과 (20.80ms, 54.8MB)
    테스트 2 〉	통과 (54.43ms, 57.5MB)
    테스트 3 〉	통과 (20.30ms, 55MB)
    테스트 4 〉	통과 (34.80ms, 57.2MB)
    테스트 5 〉	통과 (113.10ms, 60.2MB)
    테스트 6 〉	통과 (21.48ms, 54.8MB)
    테스트 7 〉	통과 (20.24ms, 59.1MB)
    테스트 8 〉	통과 (176.82ms, 62.2MB)
    테스트 9 〉	통과 (20.66ms, 55MB)
    테스트 10 〉	통과 (59.44ms, 59MB)
    테스트 11 〉	통과 (24.23ms, 56.9MB)
    테스트 12 〉	통과 (22.86ms, 54.9MB)

    순열을 이용하니 코드량은 많이 주네요... 

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    public class Solution
    {
        static List<int> numberList = new List<int>();
    
        public int solution(string numbers)
        {
        	List<int> primeList = new List<int>();
            for (int i = 1; i <= numbers.Length; i++)
                Perm(numbers.ToCharArray(), 0, numbers.Length, i);
            numberList = numberList.Distinct().ToList();
            int max = numberList.Max();
            bool[] notPrimes = new bool[max + 1];
            for (int i = 2; i <= max; i++)
            {
                if (notPrimes[i] == false)
                    primeList.Add(i);
                for (int j = i * 2; j <= max; j += i)
                    notPrimes[j] = true;
            }
            return Enumerable.Intersect(numberList, primeList).Count();
        }
    
        public static void Perm(char[] arr, int depth, int n, int k)
        {
            if (depth == k)
            {
                numberList.Add(int.Parse(string.Join("", arr.Take(k))));
                return;
            }
            for (int i = depth; i < n; i++)
            {
                char temp = arr[i];
                arr[i] = arr[depth];
                arr[depth] = temp; Perm(arr, depth + 1, n, k);
                temp = arr[i];
                arr[i] = arr[depth];
                arr[depth] = temp;
            }
        }
    }
    테스트 1 〉	통과 (19.33ms, 56.7MB)
    테스트 2 〉	통과 (40.16ms, 56MB)
    테스트 3 〉	통과 (18.02ms, 57MB)
    테스트 4 〉	통과 (29.80ms, 52.6MB)
    테스트 5 〉	통과 (155.72ms, 67.7MB)
    테스트 6 〉	통과 (19.15ms, 58.5MB)
    테스트 7 〉	통과 (19.73ms, 54.7MB)
    테스트 8 〉	통과 (275.99ms, 78.7MB)
    테스트 9 〉	통과 (19.53ms, 54.9MB)
    테스트 10 〉	통과 (45.78ms, 58.4MB)
    테스트 11 〉	통과 (22.18ms, 57.1MB)
    테스트 12 〉	통과 (19.97ms, 56.9MB)

    하지만 속도 어쩔....
    처음 어느 분의 파이썬 코드처럼 효율적으로 정리하는 방법을 찾아야 할 것 같은데....
    그건.. 다른 분들의 풀이를 참고하시길 바라며.. 

    반응형
Designed by Tistory.