Backend/โ˜•๏ธ Java

[kotlin] ๋Œ๋ ค๋Œ๋ ค ๋Œ๋ฆผํŒ~ ๋ฃฐ๋ › ๊ฒŒ์ž„ ๊ตฌํ˜„ํ•˜๊ธฐ

Hugehoo 2024. 7. 3. 13:04

๐Ÿ“Œ ์„œ๋ก 

 ๋ฃฐ๋ › ๊ฒŒ์ž„์„ ํ•œ ๋ฒˆ์ฏค ํ•ด๋ณธ ์ ์ด ์žˆ์„ ๊ฒƒ์ด๋‹ค. ๋น™๊ธ€๋น™๊ธ€ ๋Œ์•„๊ฐ€๋Š” ์›ํŒ์—๋Š” ๊ตฌ์—ญ๋ณ„๋กœ ์ƒํ’ˆ์ด ๊ฑธ๋ ค์žˆ๊ณ  ๋Œ์•„๊ฐ€๋Š” ์›ํŒ์ด ์†๋„๋ฅผ ์žƒ์„ ๋•Œ์ฏค ๋ฉˆ์ถ”๋Š” ๊ณณ์˜ ๋‹น์ฒจ ๋ง‰๋Œ€๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ƒํ’ˆ์„ ์–ป๊ฒŒ ๋œ๋‹ค.

์ด๋Ÿฐ ๋ฃฐ๋ › ๊ฒŒ์ž„์„ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ? ํ•„์ž๋Š” ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž์ด๊ธฐ์— UI๋ฅผ ๊ทธ๋ฆฌ๋Š” ๊ฒƒ๋ณด๋‹จ ์–ด๋–ค ์›๋ฆฌ์— ์˜ํ•ด ์ƒํ’ˆ์ด ๋‹น์ฒจ๋˜๋Š”์ง€ ์ดˆ์ ์„ ๋‘๊ฒŒ ๋๋‹ค. ๋งŒ์•ฝ ์›ํŒ์ด ์—ฌ์„ฏ ๊ฐœ์˜ ๊ตฌ์—ญ์œผ๋กœ ๋‚˜๋‰˜์–ด ์žˆ๋‹ค๋ฉด ๊ฐ๊ฐ 1/6์˜ ๋‹น์ฒจํ™•๋ฅ ์„ ๊ฐ€์ง„๋‹ค๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์„ธ์ƒ์ผ์ด ์›๋ž˜ ์ƒ๊ฐ๋Œ€๋กœ ๋˜๋˜๊ฐ€์š”? ์ข‹์€ ์ƒํ’ˆ์€ ํ•ญ์ƒ ๋น—๋‚˜๊ฐ€๊ณ  ์ƒ๋Œ€์ ์œผ๋กœ ๊ฐ’์–ด์น˜๊ฐ€ ๋–จ์–ด์ง€๋Š” ์ƒํ’ˆ๋งŒ ์ž์ฃผ ๊ฑธ๋ฆฌ๋Š” ๊ฒŒ ์ฐจ๊ฐ€์šด ์ž๋ณธ์ฃผ์˜์˜ ํ˜„์‹ค์ด๋‹ค like ์œค๋ฃจ์นด์Šค. ํ”„๋กœ๊ทธ๋žจ์œผ๋กœ ๊ตฌํ˜„๋œ ๋ฃฐ๋ › ๊ฒŒ์ž„์€ ๊ฐ ๊ตฌ์—ญ์˜ ๋‹น์ฒจํ™•๋ฅ ์„ ์„ธํŒ…ํ•  ์ˆ˜ ์žˆ๊ธฐ์— ๊ฐ’์–ด์น˜๊ฐ€ ๋†’์€ ์ƒํ’ˆ๋ณด๋‹จ ๊ทธ๋ ‡์ง€ ์•Š์€ ์ƒํ’ˆ์ด ๋” ๋นˆ๋ฒˆํžˆ ๋‹น์ฒจ๋  ์ˆ˜ ์žˆ๋‹ค.

์ถœ์ฒ˜ : ํ”Œ๋žซํ‹ฐ์ฝ˜

 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ๊ฐ๊ฐ์˜ ๊ตฌ์—ญ(์ƒํ’ˆ)์ด ์„œ๋กœ ๋‹ค๋ฅธ ๋‹น์ฒจํ™•๋ฅ ์„ ๊ฐ€์ง€๋Š” ๋ฃฐ๋ ›์„ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ ์˜์˜๋ฅผ ๋‘”๋‹ค. ์ถ”๊ฐ€์ ์œผ๋กœ ๊ฐ๊ฐ์˜ ๊ตฌ์—ญ์€ ์„œ๋กœ ๋‹ค๋ฅธ ์ˆ˜๋Ÿ‰์„ ์ง€๋‹Œ๋‹ค. ์•ž์œผ๋กœ๋Š” ์ด ์ˆ˜๋Ÿ‰์„ `์žฌ๊ณ `๋ผ ์นญํ•˜๊ฒ ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด ๋ฃฐ๋ ›์ด ์—ฌ์„ฏ ๊ฐœ์˜ ๊ตฌ์—ญ์œผ๋กœ ๊ตฌ์„ฑ๋œ๋‹ค๋ฉด ์—ฌ์„ฏ ๊ฐœ์˜ ๊ตฌ์—ญ์€ ๊ฐ๊ธฐ ๋‹ค๋ฅธ ์žฌ๊ณ ์™€ ๋‹น์ฒจํ™•๋ฅ ์„ ๋ณด์œ ํ•œ ์…ˆ์ด๋‹ค. ๋•Œ๋ฌธ์— ๋‹น์ฒจํ™•๋ฅ ์ด ๋†’์€ ๊ตฌ์—ญ์˜ ์•„์ดํ…œ์ด ๋จผ์ € ์†Œ์ง„๋˜๊ณ , ๋‹น์ฒจํ™•๋ฅ ์ด ๋‚ฎ์€ ๊ตฌ์—ญ์˜ ์•„์ดํ…œ์€ ๋น„๊ต์  ๋Šฆ๊ฒŒ ์žฌ๊ณ ๊ฐ€ ์†Œ์ง„ ๋  ๊ฒƒ์ด๋‹ค.

 

 

๐Ÿ“Œ ์š”๊ตฌ์‚ฌํ•ญ ๋ฐ ๊ณ ๋ ค์‚ฌํ•ญ

์•„์ดํ…œ(score) ํ™•๋ฅ  ์žฌ๊ณ 
10 0.5 40
50 0.3 30
100 0.1 16
500 0.05 8
800 0.03 4
1000 0.02 2

 

๋ฃฐ๋ › ๊ฒŒ์ž„์˜ ์š”๊ตฌ์‚ฌํ•ญ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค

  • ๋‹น์ฒจ๋  ์ˆ˜ ์žˆ๋Š” ์•„์ดํ…œ์˜ ์ˆ˜๋Ÿ‰์€ ์ด 100๊ฐœ๋กœ ํ•œ์ •ํ•œ๋‹ค.
  • ๊ฐ ์•„์ดํ…œ์˜ ๋‹น์ฒจ ํ™•๋ฅ ์€ ์œ„์˜ ํ‘œ(table)์™€ ๊ฐ™๋‹ค.
  • ์ฆ‰ ๋ฃฐ๋ ›์˜ ์‹คํ–‰ ํšŸ์ˆ˜ ์—ญ์‹œ 100๋ฒˆ์œผ๋กœ ์ œํ•œ๋˜๋ฉฐ 100๋ฒˆ์˜ ํ”Œ๋ ˆ์ด ์ดํ›„์—๋Š” ๋ฃฐ๋ ›์— ๋‚จ์€ ์•„์ดํ…œ ์žฌ๊ณ ๊ฐ€ ์—†์–ด์•ผํ•œ๋‹ค.
  • ๊ฐ๊ฐ์˜ ์žฌ๊ณ ๋Š” ๋™์‹œ์„ฑ ์ด์Šˆ๋กœ๋ถ€ํ„ฐ ์ž์œ ๋กœ์›Œ์•ผ ํ•œ๋‹ค.
  • ์ „์ฒด ์•„์ดํ…œ์˜ ๋‹น์ฒจ ํ™•๋ฅ ์„ ํ•ฉ์‚ฐํ•˜๋ฉด 1์ด ๋œ๋‹ค.
  • ํŠน์ • ์•„์ดํ…œ์˜ ์ˆ˜๋Ÿ‰์ด ๋ชจ๋‘ ์†Œ์ง„๋˜๋ฉด ํ•ด๋‹น ์•„์ดํ…œ์˜ ๋‹น์ฒจํ™•๋ฅ ์„ ์žฌ๊ณ ๊ฐ€ ์†Œ์ง„๋˜์ง€ ์•Š์€ ๋‚˜๋จธ์ง€ ์•„์ดํ…œ์— ๋ถ„๋ฐฐํ•œ๋‹ค.

ex) 10์  ์•„์ดํ…œ ์žฌ๊ณ  ๋ชจ๋‘ ์ˆ˜๋Ÿ‰ -> 0.5 / 5(๋‚จ์€ ์•„์ดํ…œ ๊ฐœ์ˆ˜) = 0.1 โžก๏ธ ๋‚จ์€ ์•„์ดํ…œ์˜ ํ™•๋ฅ ์— ๊ฐ๊ฐ ํ•ฉ์‚ฐ (์•„๋ž˜ ๋„ํ‘œ ์ฐธ์กฐ)

 

์ ์ˆ˜(score) ํ™•๋ฅ  ์žฌ๊ณ 
10 0.5 40
50 0.3 + 0.1 = 0.4 30
100 0.1 + 0.1 = 0.2 16
500 0.05 + 0.1 = 0.15  8
800 0.03 + 0.1 = 0.13 4
1000 0.02 + 0.1 = 0.12 2

 

๐Ÿ“Œ ๊ตฌํ˜„ํ•˜๊ธฐ

 

๊ธฐ์ˆ  ์ŠคํŽ™
- Kotlin 1.8
- Junit 5.10
- ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„  ๋ณ„๋„์˜ ์™ธ๋ถ€ DB ์—ฐ๊ฒฐ์—†์ด ๋ฉ”๋ชจ๋ฆฌ ์ƒ์—์„œ๋งŒ ๋ฃฐ๋ › ํด๋ž˜์Šค๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค.

 

์šฐ์„  ๋ฃฐ๋ › ๋Œ๋ฆผํŒ์„ ๊ตฌ์„ฑํ•˜๋Š” ๊ฐ๊ฐ์˜ ๊ตฌ์—ญ์„ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

์œ„ ์š”๊ตฌ์‚ฌํ•ญ์— ๋”ฐ๋ฅด๋ฉด ๋ฃฐ๋ ›์˜ ๊ฐ ๊ตฌ์—ญ์€ 1) ์ ์ˆ˜(score) 2) ๋‹น์ฒจ ํ™•๋ฅ (probability) 3) ์žฌ๊ณ (stocks) ์ด 3๊ฐ€์ง€ ์š”์†Œ๋ฅผ ๊ฐ–๋Š”๋‹ค. ์ด๋ฅผ ์ฝ”ํ‹€๋ฆฐ ์ฝ”๋“œ๋กœ ํ‘œํ˜„ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

 

๊ฐ ๊ตฌ์—ญ์„ ์ฝ”๋“œ๋กœ ๋‚˜ํƒ€๋‚ด๊ธฐ - Sector.kt

class Sector(val score: Int, val probability: BigDecimal, var stocks: AtomicInteger) {
    fun decreaseStock() {
        if (stocks.get() > 0) {
            stocks.set(stocks.get() - 1)
        }
    }
}

 

ํ•ด๋‹น ํด๋ž˜์Šค๋Š” 3๊ฐœ์˜ ํ•„๋“œ์™€ decreaseStock() ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ–๋Š”๋‹ค. ๋ฃฐ๋ ›์„ ํ•œ๋ฒˆ ๋Œ๋ฆด ๋•Œ๋งˆ๋‹ค ์ตœ์†Œ ํ•˜๋‚˜์˜ ์•„์ดํ…œ์€ ๋‹น์ฒจ๋˜๊ธฐ์— ์žฌ๊ณ ์˜ ๊ฐฏ์ˆ˜๋Š” ํ•˜๋‚˜์”ฉ ์ฐจ๊ฐ๋˜์•ผ ํ•œ๋‹ค. stocks ํ•„๋“œ๋Š” AtomicInteger ๋กœ ์ •์˜ํ–ˆ๋‹ค. ์ฒ˜์Œ์—” Int ํƒ€์ž…์œผ๋กœ ์„ ์–ธํ–ˆ์ง€๋งŒ ๋™์‹œ์„ฑ์„ ์ง€์ผœ์•ผ ํ•˜๋Š” ์š”๊ตฌ์‚ฌํ•ญ์ด ์žˆ๋‹ค. ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ stocks ๋ฅผ ์ฐจ๊ฐํ•œ๋‹ค๋ฉด stocks ํ•„๋“œ๋Š” ๊ฒฝ์Ÿ์ƒํƒœ๊ฐ€ ๋˜์–ด ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ์ด ๋งž์ง€ ์•Š๋Š” ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ณ ์ž stocks ์˜ ํƒ€์ž…์€ AtomicInteger ๋กœ ๋ณ€๊ฒฝํ–ˆ๋‹ค.

 

๊ฐ๊ฐ์˜ Sector ๋ฅผ Collection ์œผ๋กœ ๋ฌถ์–ด ํ•˜๋‚˜์˜ ๋ฃฐ๋ ›์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ํ•„์ž๋Š” List collection ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ตœ์ดˆ์— ๋ฃฐ๋ › ๊ฒŒ์ž„์„ ์‹คํ–‰ํ•˜๋Š” ์ƒํƒœ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. (ํ•œ๊ธ€๋ช… ๋ฉ”์„œ๋“œ๋Š” ์ดํ•ด๋ฅผ ๋•๊ธฐ ์œ„ํ•ด.. ใ…Žใ…Ž)

internal class RouletteUnitTest {
    private fun ๋ฃฐ๋ ›_์ดˆ๊ธฐํ™”(): List<Sector> {
        return listOf(
            Sector(10, BigDecimal.valueOf(0.5), AtomicInteger(40)),
            Sector(50, BigDecimal.valueOf(0.3), AtomicInteger(30)),
            Sector(100, BigDecimal.valueOf(0.1), AtomicInteger(16)),
            Sector(500, BigDecimal.valueOf(0.05), AtomicInteger(8)),
            Sector(800, BigDecimal.valueOf(0.03), AtomicInteger(4)),
            Sector(1000, BigDecimal.valueOf(0.02), AtomicInteger(2))
        )
    }
}

 

๐Ÿ“Œ ํ”Œ๋ ˆ์ด ๊ณผ์ • ๊ตฌํ˜„ํ•˜๊ธฐ

์š”๊ตฌ์‚ฌํ•ญ์„ ๋‹ค์‹œ ์‚ดํŽด๋ณด์ž. ๊นŒ๋‹ค๋กœ์šด ๋ถ€๋ถ„์ด ํ•˜๋‚˜ ์žˆ๋‹ค.

๋‹ค์‹œ๋ณด๋Š” ์š”๊ตฌ์‚ฌํ•ญ

 

ํŠน์ • ์•„์ดํ…œ์˜ ์žฌ๊ณ ๊ฐ€ ๋ชจ๋‘ ์†Œ์ง„๋˜๋ฉด ํ•ด๋‹น ์•„์ดํ…œ์˜ ๋‹น์ฒจ ํ™•๋ฅ ์„ ๋‹ค๋ฅธ ๊ณณ์œผ๋กœ ์žฌ๋ถ„๋ฐฐ ํ•ด์•ผํ•œ๋‹ค. ์ „์ฒด ๋ฃฐ๋ ›์˜ ๋‹น์ฒจ ํ™•๋ฅ ์„ 1๋กœ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋‹ค.

์œ„ ์ผ€์ด์Šค๋ณด๋‹ค ๋” ๊ฐ„๋‹จํ•œ ์˜ˆ๋ฅผ ๋“ค์–ด๋ณด์ž. ์ด 3๊ฐœ์˜ sector ๊ฐ€ ์žˆ๊ณ  ๊ฐ๊ฐ์˜ ๋‹น์ฒจ ํ™•๋ฅ ์€ 20%, 30%, 50% ๋กœ ๊ฐ€์ •ํ•œ๋‹ค. ์ด๋ฅผ ๊ฐ„๋‹จํžˆ ์ •์ˆ˜๋กœ ํ‘œํ˜„ํ•˜๋ฉด ์•„๋ž˜์˜ ์ด๋ฏธ์ง€์™€ ๊ฐ™๋‹ค.

 

 

0 ์—์„œ 9 ๊นŒ์ง€(์ด 10๊ฐœ์˜ ์ •์ˆ˜) ๋žœ๋คํ•œ ์ •์ˆ˜๋ฅผ ํ•˜๋‚˜ ๋ฝ‘์„ ๋•Œ, ๋‹น์ฒจ๋œ ์ •์ˆ˜๊ฐ€ ์†ํ•œ ๋ฒ”์œ„์˜ ์•„์ดํ…œ์„ ๋ฐ›๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋ฏธ์ง€ ์ƒ ๋‹น์ฒจํ™•๋ฅ ์ด ๋†’์€ ์•„์ดํ…œ ์ˆœ์„œ๋Œ€๋กœ ์˜์—ญ์ด ๋„“์€ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿฌํ•œ ์˜์—ญ์ด range ๋กœ ๊ฐ„์ฃผ๋˜๋ฉฐ ๋žœ๋ค์œผ๋กœ ๋ฝ‘์€ ์ˆ˜๊ฐ€ ์–ด๋–ค range ์— ์†ํ•˜๋Š”์ง€์— ๋”ฐ๋ผ ๋‹น์ฒจ ์•„์ดํ…œ์ด ๋‹ฌ๋ผ์ง„๋‹ค.

 

 

 

์ด๋Ÿฌํ•œ ์›๋ฆฌ๋ฅผ ๋ฃฐ๋ › ๊ฒŒ์ž„์— ์ ์šฉํ•ด๋ณด์ž. ๋ฃฐ๋ ›์€ ์ด 6๊ฐœ์˜ ์•„์ดํ…œ์ด ์žˆ์œผ๋ฏ€๋กœ ์ด 6๊ฐœ์˜ ๋ฒ”์œ„(range)์ด ์ƒ๊ธฐ๋Š” ์…ˆ์ด๋‹ค. ๋ฃฐ๋ ›์˜ ๋ˆ„์  ํ™•๋ฅ  ๊ตฌ๊ฐ„์„ ๋‚˜ํƒ€๋‚ด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•ํƒœ์ผ ๊ฒƒ์ด๋‹ค.

 

๐Ÿ’Ž Item 10: 0 < x <= 0.5

๐Ÿ’Ž Item 50:  0.5 < x <= 0.8

๐Ÿ’Ž Item 100: 0.8 < x <= 0.9

๐Ÿ’Ž Item 500: 0.9 < x <= 0.95

๐Ÿ’Ž Item 800: 0.95 < x <= 0.98

๐Ÿ’Ž Item 1000: 0.98 < x <= 1

 

0 ์—์„œ 1 ์‚ฌ์ด์˜ ๋žœ๋คํ•œ ์ˆ˜๋ฅผ ๊ตฌํ•˜๊ณ , ํ•ด๋‹น ์ˆซ์ž(ex, 0.32)๊ฐ€ ์†ํ•˜๋Š” ๋ฒ”์œ„์— ๋”ฐ๋ผ ์•„์ดํ…œ์ด ๋‹น์ฒจ๋˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด ๊ฒฝ์šฐ์—๋Š” 0 < x <= 0.5 ์— ์†ํ•˜๋ฏ€๋กœ item 10 ์„ ์–ป๊ฒŒ ๋œ๋‹ค. 0 ์—์„œ 1 ์‚ฌ์ด์˜ ๋žœ๋คํ•œ ์ˆซ์ž๋Š” Random ๋ฉ”์„œ๋“œ๋กœ ๊ฐ„๋‹จํžˆ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

๋žœ๋ค ๊ฐ’์ด ์–ด๋Š ๋ฒ”์œ„์— ์†ํ•˜๋Š”์ง€์— ๋”ฐ๋ผ ๋‹น์ฒจ ์•„์ดํ…œ์ด ๋‹ฌ๋ผ์ง„๋‹ค.

์œ„ ์ด๋ฏธ์ง€์ฒ˜๋Ÿผ ๊ฐ ์•„์ดํ…œ๋ณ„ ๋ˆ„์  ๋‹น์ฒจํ™•๋ฅ ์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด 1) ์•„์ดํ…œ์˜ ๋‹น์ฒจํ™•๋ฅ ์„ ์ฐจ๋ก€๋กœ ๋ˆ„์ ํ•˜๊ณ  2) ๋ˆ„์ ๋œ ๋‹น์ฒจํ™•๋ฅ ์„ key๋กœ ํ•˜์—ฌ ๊ฐ ์•„์ดํ…œ์„ ํ• ๋‹นํ•ด์•ผ ํ•œ๋‹ค. ํ•„์ž๋Š” TreeMap ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์ด์šฉํ•ด ์•„์ดํ…œ์˜ ๋‹น์ฒจํ™•๋ฅ ์„ ์ฐจ๋ก€๋กœ ๋ˆ„์ ํ–ˆ๋‹ค. (TreeMap ์„ ์„ ํƒํ•œ ์ด์œ ๋Š” ์•„๋ž˜์—์„œ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค.)

class RouletteGame(val sectorList: List<Sector>) {

    private fun getRouletteProbability(): TreeMap<BigDecimal, Sector> {
        var cumulativeProbability = BigDecimal.ZERO // (a)
        val probabilityMap = TreeMap<BigDecimal, Sector>() // (b)

        this.sectorList
            .forEach { sector ->
                cumulativeProbability = cumulativeProbability.add(sector.probability) // (c)
                probabilityMap[cumulativeProbability] = sector // (d)
            }
        return probabilityMap
    }
}

 

 

(a) ์ตœ์ดˆ์˜ ๋ˆ„์  ํ™•๋ฅ  ๊ฐ’์€ 0 ์ด์–ด์•ผ ํ•œ๋‹ค. ๊ฐ ์•„์ดํ…œ์˜ ๋‹น์ฒจ ํ™•๋ฅ ์„ ์ด ๊ฐ’์— ์ฐจ๋ก€๋กœ ๋ˆ„์ ํ•˜๊ฒŒ ๋œ๋‹ค.

(b) ๊ฐ ๋ˆ„์  ํ™•๋ฅ ์„ ์ €์žฅํ•  ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ TreeMap ํƒ€์ž…์œผ๋กœ ์„ ์–ธํ•˜์—ฌ key ๋Š”  ๋ˆ„์ ํ™•๋ฅ ์„, value ๋Š” Sector ์ธ์Šคํ„ด์Šค๋ฅผ ์ €์žฅํ•œ๋‹ค.

 

(c) sectorList ๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ ๊ฐ Sector ์˜ ๋‹น์ฒจํ™•๋ฅ ์„ cumlativeProbability ์— ์ฐจ๋ก€๋กœ ๋ˆ„์ ํ•œ๋‹ค.

(d) ๋ˆ„์ ๋œ ๋‹น์ฒจํ™•๋ฅ ์„ probabilityMap ์˜ key ๋กœ ์ €์žฅํ•˜๋ฉฐ value ์—” Sector ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•œ๋‹ค.

 

๊ฒฐ๊ณผ์ ์œผ๋กœ ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋Š” probabilityMap ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์•„์ดํ…œ์˜ ๋‹น์ฒจํ™•๋ฅ ์„ ์ฐจ๋ก€๋กœ ๋ˆ„์ ํ•˜๊ณ  ์ด๋ฅผ key๋กœ ๊ฐ€์ง€๋Š” ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ–ˆ๋‹ค.

 

 

๐Ÿ“Œ TreeMap ์‚ฌ์šฉ ์ด์œ ์™€ ๊ตฌํ˜„

 

์ €์žฅํ•˜๋Š” Key์— ํŠน๋ณ„ํ•œ ์ˆœ์„œ๊ฐ€ ์—†๋Š” HashMap๊ณผ ๋‹ฌ๋ฆฌ TreeMap์€ ์–ด๋–ค ๊ทœ์น™์— ๋”ฐ๋ผ ํ‚ค๋ฅผ ์ •๋ ฌํ•˜์—ฌ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค. TreeMap ์€ ์ด์ง„ ํƒ์ƒ‰ ํŠธ๋ฆฌ ํ˜•ํƒœ๋ฅผ ํ™œ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ •๋ ฌ๋œ key ์ˆœ์„œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค. TreeMap ์€ ํ‚ค ๊ฐ’์— ๋”ฐ๋ผ ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์ž๋™ ์ •๋ ฌ๋˜๊ธฐ ๋•Œ๋ฌธ์— (์˜ˆ๋ฅผ ๋“ค์–ด) 0.1, 0.36, 0.05, 0.52 ๋ฅผ key๋กœ ์ €์žฅํ•˜๋ฉด  0.05, 0.1, 0.36, 0.52 ์ˆœ์„œ๋กœ ์ž๋™ ์ •๋ ฌ ์ €์žฅ๋œ๋‹ค. TreeMap ์˜ ์ด๋Ÿฌํ•œ ํŠน์ง•์„ ํ™œ์šฉํ•˜์—ฌ ๊ฐ Sector ์˜ ๋‹น์ฒจํ™•๋ฅ ์„ ์ €์žฅํ•˜๋ฉด ์ •๋ ฌ๋œ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋˜ํ•œ TreeMap ์˜ ๋‚ด๋ถ€ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด NavigableMap ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†ํ•œ ๊ตฌํ˜„์ฒด์ž„์„ ์•Œ ์ˆ˜ ์žˆ๋Š”๋ฐ, NavigableMap ์˜ higherEntry() ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ์š”๊ตฌ์‚ฌํ•ญ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

TreeMap

 

higherEntry() method

 

higherEntry() : ์ธ์ž๋กœ ๋ฐ›์€ key ๋ณด๋‹ค ํฐ key ์ค‘์—์„œ ๊ฐ€์žฅ ์ž‘์€ key ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

higherEntry()๋Š” NavigableMap ์ธํ„ฐํŽ˜์ด์Šค์— ์„ ์–ธ๋œ ์ถ”์ƒ ๋ฉ”์„œ๋“œ๋กœ TreeMap์—์„œ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ๋ผ ์žˆ๋‹ค.  

higherEntry() ๋ฉ”์„œ๋“œ๋Š” ์ฃผ์–ด์ง„ key ๋ณด๋‹ค ํฐ ๋‹ค์Œ(next) key ์˜ value ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” ํŠน์ • key ์˜ ๋‹ค์Œ ํ•ญ๋ชฉ์„ ์ฐพ๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋ฉฐ ๋งŒ์•ฝ ์ฃผ์–ด์ง„ key ๋ณด๋‹ค ํฐ ๋‹ค์Œ key ๋ฅผ ๊ฐ€์ง„ ํ•ญ๋ชฉ์ด ์—†์œผ๋ฉด null์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด TreeMap ์— ์ €์žฅ๋œ key ์ค‘ ๊ฐ€์žฅ ํฐ ๊ฐ’์ด 1์ผ ๋•Œ higherEntry(2) ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด 2 ๋ณด๋‹ค ํฐ key ๋ฅผ ๊ฐ€์ง„ ํ•ญ๋ชฉ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— null ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. 

 

class RouletteGame(val sectorList: List<Sector>) {


    private fun getResult(sectorMap: TreeMap<BigDecimal, Sector>): Sector {
        val random = Random()
        val randomValue = BigDecimal.valueOf(random.nextDouble())
        return sectorMap.higherEntry(randomValue).value
    }

    private fun getRouletteProbability(): TreeMap<BigDecimal, Sector> {
        var cumulativeProbability = BigDecimal.ZERO
        val probabilityMap = TreeMap<BigDecimal, Sector>()

        this.sectorList
            .forEach { sector ->
                cumulativeProbability = cumulativeProbability.add(sector.probability)
                probabilityMap[cumulativeProbability] = sector
            }
        return probabilityMap
    }
}

 

๋‹น์ฒจ ๊ฒฐ๊ณผ๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด getResult() ๋ฉ”์„œ๋“œ๋ฅผ ์„ ์–ธํ–ˆ๋‹ค.

ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋Š” getRouletteProbability() ๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ๋œ TreeMap ์„ ์ธ์ž๋กœ ๋ฐ›๊ณ , ๋‚ด๋ถ€์—์„œ Random ์ˆซ์ž๋ฅผ ๋งŒ๋“ค์–ด higherEntry()  ๋ฉ”์„œ๋“œ์˜ ์ธ์ž๋กœ ์‚ฌ์šฉํ•œ๋‹ค. higherEntry(randomValue)๋Š” TreeMap์˜ key ์ค‘ randomValue๋ณด๋‹ค ํฌ๋ฉด์„œ ๋™์‹œ์— ๊ฐ€์žฅ ๊ทผ์ ‘ํ•œ key ๋ฅผ ์„ ํƒํ•ด ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด randomValue ๊ฐ’์ด 0.55 ์ผ ๋•Œ 0.55 ๋ณด๋‹ค ํฌ๋ฉด์„œ ๊ฐ€์žฅ ๊ทผ์ ‘ํ•œ ๊ฐ’์ธ 0.8์„ key ๋กœ ๊ฐ–๋Š” value ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋žœ๋ค ๊ฐ’์€ Random ํด๋ž˜์Šค์˜ nextDouble ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค. ์ฝ”ํ‹€๋ฆฐ ๊ณต์‹ ๋ฌธ์„œ์— ์˜ํ•˜๋ฉด nextDouble() ๋ฉ”์„œ๋“œ๋Š” 0 <= x < 1 ๋ฒ”์œ„์˜ Double ํ˜• ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

TreeMap ์˜ key ๊ฐ€ ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์ €์žฅ๋˜๋Š” ํŠน์ง•์„ ํ™œ์šฉํ•ด ํ™•๋ฅ map ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

 

๐Ÿ“Œ ์žฌ๊ณ  ์ฐจ๊ฐ์„ ๊ณ ๋ คํ•œ ํ™•๋ฅ  ์—ฐ์‚ฐ

์š”๊ตฌ์‚ฌํ•ญ์—์„œ ์žฌ๊ณ  ๋ถ€๋ถ„์„ ๋‹ค์‹œ ํ™•์ธํ•ด๋ณด์ž. ์ด ์žฌ๊ณ ๋Š” 100๊ฐœ๋กœ ๊ฐ ์•„์ดํ…œ์˜ ์žฌ๊ณ ๊ฐ€ ๋ชจ๋‘ ์†Œ์ง„๋˜๋ฉด ํ•ด๋‹น ์•„์ดํ…œ์˜ ๋‹น์ฒจ ํ™•๋ฅ ์„ ๋‹ค๋ฅธ ์•„์ดํ…œ์— ๋ถ„๋ฐฐํ•ด์•ผ ํ•œ๋‹ค. ๋งŒ์•ฝ ๋‹น์ฒจํ™•๋ฅ ์ด 0.5์ธ ์•„์ดํ…œ10 ์ด ๋ชจ๋‘ ์†Œ์ง„๋˜๋ฉด ๋‚จ์€ ์•„์ดํ…œ(5๊ฐ€์ง€ ์ข…๋ฅ˜)์— ํ™•๋ฅ ์„ ๊ฐ๊ฐ ๋ถ„๋ฐฐํ•ด์•ผํ•œ๋‹ค. ์ด ๊ฒฝ์šฐ์—๋Š” ๋‚จ์€ ์•„์ดํ…œ์˜ ๊ฐฏ์ˆ˜๊ฐ€ 5๊ฐœ์ด๋ฏ€๋กœ ๋ถ„๋ฐฐํ•ด์•ผ ํ•  ํ™•๋ฅ  ๊ฐ’์€ 0.5 / 5 = 0.1 ์ด ๋œ๋‹ค. ์ด๋ฅผ ๊ณ ๋ คํ•˜์—ฌ ํ™•๋ฅ  map ์„ ๋‹ค์‹œ ๊ตฌํ˜„ํ•ด๋ณด์ž.

class RouletteGame(val sectorList: List<Sector>) {

    private val soldOuts = ConcurrentHashMap<Int, Roulette>()

    private fun getCurrentProbability(): BigDecimal? {
        val sumSoldOutProbability = soldOuts.values
            .stream()
            .map { obj: Roulette -> obj.probability }
            .reduce(BigDecimal.ZERO)
            { obj: BigDecimal, agent: BigDecimal? -> obj.add(agent) }
        val availableSize = rouletteList.size - soldOuts.size
        return sumSoldOutProbability.divide(BigDecimal.valueOf(availableSize.toLong()), 3, RoundingMode.HALF_UP)
    }

    ...
    
}

 

getCurrentProbability() ๋Š” ์žฌ๊ณ ๊ฐ€ ์†Œ์ง„๋œ ์•„์ดํ…œ์˜ ํ™•๋ฅ ์„ ๋‹ค๋ฅธ ์•„์ดํ…œ์—๊ฒŒ ์žฌ๋ถ„๋ฐฐ ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋‹ค.

์ด ๋•Œ soldOuts ๋ผ๋Š” ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋ฅผ ํ™œ์šฉํ•˜์—ฌ ํ˜„์žฌ ๋ฃฐ๋ › ๊ฒŒ์ž„์—์„œ ์žฌ๊ณ ๊ฐ€ ์†Œ์ง„๋œ ์•„์ดํ…œ์„ ์ €์žฅํ•œ๋‹ค. ํ•ด๋‹น ๋ณ€์ˆ˜๋Š” ConcurrentHashMap<Int, Roulette> ํƒ€์ž…์œผ๋กœ ์†”๋“œ ์•„์›ƒ๋œ ์•„์ดํ…œ์„ ์ €์žฅํ•œ๋‹ค.

 

์ด์ œ ์œ„์—์„œ ์‚ดํŽด๋ณธ getRouletteProbability() ๋ฉ”์„œ๋“œ์— getCurrentProbability() ๋ฉ”์„œ๋“œ๋ฅผ ๋…น์—ฌ๋ณด์ž. remainProbabiliy ๋Š” ์•„์ง ์žฌ๊ณ ๊ฐ€ ๋‚จ์•„์žˆ๋Š” ์•„์ดํ…œ์—๊ฒŒ ๋ถ„๋ฐฐํ•ด์ค„ ํ™•๋ฅ  ๊ฐ’์ด๋‹ค. ์•„์ง ์žฌ๊ณ ๊ฐ€ ๋‚จ์•„์žˆ๋Š” ์•„์ดํ…œ์„ ๊ตฌํ•˜๊ธฐ ์œ„ํ•ด filter() ๋ฅผ ์ถ”๊ฐ€(a)ํ•˜๊ณ , ํ•„ํ„ฐ๋ง ๋œ ์žฌ๊ณ ์˜ ์•„์ดํ…œ์— remainProbability ๋ฅผ ๋”ํ•ด์ค€๋‹ค(b).

class RouletteGame(val rouletteList: List<Roulette>) {

    private val soldOuts = ConcurrentHashMap<Int, Roulette>()
    private val random = Random()
    
    ...
    
    private fun getRouletteProbability(): TreeMap<BigDecimal, Roulette> {
        val remainProbability = getCurrentProbability()
        var cumulativeProbability = BigDecimal.ZERO
        val probabilityMap = TreeMap<BigDecimal, Roulette>()

        this.rouletteList
            .filter { r -> r.stocks.get() != 0 } // (a)
            .forEach { r ->
                val add = r.probability.add(remainProbability) // (b)
                cumulativeProbability = cumulativeProbability.add(add)
                probabilityMap[cumulativeProbability] = r
            }
        return probabilityMap
    }
    
    private fun getCurrentProbability(): BigDecimal? {
        val sumSoldOutProbability = soldOuts.values
            .stream()
            .map { obj: Roulette -> obj.probability }
            .reduce(BigDecimal.ZERO)
            { obj: BigDecimal, agent: BigDecimal? -> obj.add(agent) }
        val availableSize = rouletteList.size - soldOuts.size
        return sumSoldOutProbability.divide(BigDecimal.valueOf(availableSize.toLong()), 3, RoundingMode.HALF_UP)
    }
    
    ...
    
}

 

 

๐Ÿ“Œ play() ๋ฉ”์„œ๋“œ

์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ๋ชจ๋“  ๋‚ด์šฉ์„ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ด play() ๋ฉ”์„œ๋“œ๋กœ, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฃฐ๋ ›์„ ํ•œ๋ฒˆ ๋Œ๋ฆด ๋•Œ ํ˜ธ์ถœ๋œ๋‹ค.

getResult() ๋ฉ”์„œ๋“œ์˜ ๊ฒฐ๊ณผ๋กœ ๋‚˜์˜จ roulette ๊ฐ’์€ ๋‹น์ฒจ๋œ ์•„์ดํ…œ์— ํ•ด๋‹น๋˜๋ฉฐ, ์ด๋ฅผ decreaseStock() ๋ฉ”์„œ๋“œ์˜ ์ธ์ž๋กœ ํ˜ธ์ถœํ•˜์—ฌ ํ•ด๋‹น ์•„์ดํ…œ์˜ ์žฌ๊ณ ๋ฅผ ์ฐจ๊ฐํ•œ๋‹ค. ์ด ๋•Œ ํ•ด๋‹น ์•„์ดํ…œ(roulette) ์˜ stocks ๊ฐ€ 0์ด๋ฉด soldOut ์œผ๋กœ ํŒ๋‹จํ•˜์—ฌ  soldOuts concurrentHashMap ์— ์ถ”๊ฐ€ํ•œ๋‹ค.

 

class RouletteGame(val rouletteList: List<Roulette>) {

    private val soldOuts = ConcurrentHashMap<Int, Roulette>()
    private val random = Random()
    
    fun play(): Roulette {
        checkPlayAvailable()
        val rouletteProbability = getRouletteProbability()
        val roulette = getResult(rouletteProbability)
        decreaseStock(roulette)
        return roulette
    }
    
    private fun checkPlayAvailable() {
        if (this.rouletteList.size == this.soldOuts.size) {
            throw IllegalStateException()
        }
    }
    
    private fun getResult(rouletteMap: TreeMap<BigDecimal, Roulette>): Roulette {
        val randomValue = BigDecimal.valueOf(random.nextDouble())
        return rouletteMap.higherEntry(randomValue).value
    }
    
    private fun decreaseStock(roulette: Roulette) {
        roulette.decreaseStock()
        if (roulette.stocks.get() == 0) {
            soldOuts[roulette.score] = roulette
        }
    }
    
    ...
    
}

 

 

๐Ÿ“Œ ๊ฒฐ๋ก 

 

์ด๋ ‡๊ฒŒ ๋Œ๋ ค๋Œ๋ ค ๋Œ๋ฆผํŒ์„ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค. ์†Œ์ง„๋œ ์•„์ดํ…œ์˜ ํ™•๋ฅ ์„ ๋‹ค๋ฅธ ์•„์ดํ…œ์œผ๋กœ ๋ถ„๋ฐฐํ•˜๋Š” ์š”๊ตฌ์‚ฌํ•ญ์ด ๊นŒ๋‹ค๋กญ๊ธด ํ–ˆ์ง€๋งŒ TreeMap ์ž๋ฃŒํ˜•์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” soldOuts ๋ณ€์ˆ˜๋ฅผ concurrentHashMap ํƒ€์ž…์œผ๋กœ ์„ ์–ธํ•œ ๋•๋ถ„์— ๋™์‹œ์„ฑ ๊ด€๋ จ ๋ฌธ์ œ๋„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ๋™์‹œ์„ฑ ๋ฌธ์ œ์— ๊ด€ํ•œ ์ฝ”๋“œ๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋กœ ๋Œ€์ฒดํ•œ๋‹ค.

ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋Š” ์•„๋ž˜์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

 

๐Ÿ“Œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

๋”๋ณด๊ธฐ
internal class RouletteUnitTest {
    private val initialStock = 100
    private fun ๋ฃฐ๋ ›_์ดˆ๊ธฐํ™”(): List<Roulette> {
        return listOf(
            Roulette(10, BigDecimal.valueOf(0.5), AtomicInteger(40)),
            Roulette(50, BigDecimal.valueOf(0.3), AtomicInteger(30)),
            Roulette(100, BigDecimal.valueOf(0.1), AtomicInteger(16)),
            Roulette(500, BigDecimal.valueOf(0.05), AtomicInteger(8)),
            Roulette(800, BigDecimal.valueOf(0.03), AtomicInteger(4)),
            Roulette(1000, BigDecimal.valueOf(0.02), AtomicInteger(2))
        )
    }

    @Test
    @DisplayName("๋ฃฐ๋ › ๊ฒŒ์ž„ ํ”Œ๋ ˆ์ด ํ…Œ์ŠคํŠธ")
    fun playTest() {
        val initialRoulette = ๋ฃฐ๋ ›_์ดˆ๊ธฐํ™”()
        val rouletteGame = RouletteGame(initialRoulette)
        for (i in 0 until initialStock) {
            val play = rouletteGame.play()
            if (play.stocks.get() == 0) {
                println(i.toString() + "๋ฒˆ์งธ play: " + play.score + "์  ์•„์ดํ…œ ์†Œ์ง„")
            }
        }
        val rouletteList = rouletteGame.rouletteList
        for (roulette in rouletteList) {
            Assertions.assertThat(roulette.stocks.get()).isEqualTo(0)
        }
    }

    @Test
    @DisplayName("ํ”Œ๋ ˆ์ด ๋งˆ๋‹ค ์žฌ๊ณ ๋Š” 1์”ฉ ์ฐจ๊ฐ๋œ๋‹ค")
    fun stockCountTest() {
        val initialRoulette = ๋ฃฐ๋ ›_์ดˆ๊ธฐํ™”()
        val rouletteGame = RouletteGame(initialRoulette)
        val totalStock = rouletteGame.currentTotalStock()
        Assertions.assertThat(totalStock).isEqualTo(initialStock)
        for (i in 1..initialStock) {
            rouletteGame.play()
            val currentStock = rouletteGame.currentTotalStock()
            Assertions.assertThat(currentStock).isEqualTo(initialStock - i)
        }
    }

    @Test
    @DisplayName("์žฌ๊ณ  ์—†์„๋•Œ ์˜ˆ์™ธ๋ฅผ ๋˜์ง„๋‹ค")
    fun overPlayTest() {
        val initialRoulette = ๋ฃฐ๋ ›_์ดˆ๊ธฐํ™”()
        val rouletteGame = RouletteGame(initialRoulette)
        for (i in 1..100) {
            rouletteGame.play()
        }
        Assertions.assertThatThrownBy { rouletteGame.play() }
    }

    @Test
    @DisplayName("์Šค๋ ˆ๋“œ 30๊ฐœ ๋™์‹œ์„ฑ ํ…Œ์ŠคํŠธ - Future")
    @Throws(
        InterruptedException::class,
        ExecutionException::class
    )
    fun multiThreadTest() {
        val initialRoulette = ๋ฃฐ๋ ›_์ดˆ๊ธฐํ™”()
        val initialStock = initialRoulette.stream()
            .mapToInt { r -> r.stocks.get() }
            .sum()
        val rouletteGame = RouletteGame(initialRoulette)
        val numThreads = 30
        val executorService = Executors.newFixedThreadPool(numThreads)
        for (i in 0 until numThreads) {
            val future = executorService.submit<Roulette> { rouletteGame.play() }
            future.get()
        }
        executorService.shutdown()
        Assertions.assertThat(initialStock - numThreads)
            .isEqualTo(rouletteGame.currentTotalStock())
    }

    @Test
    @DisplayName("์Šค๋ ˆ๋“œ 30๊ฐœ ๋™์‹œ์„ฑ ํ…Œ์ŠคํŠธ - Thread")
    @Throws(
        InterruptedException::class
    )
    fun multiThreadTestV2() {
        val initialRoulette = ๋ฃฐ๋ ›_์ดˆ๊ธฐํ™”()
        val rouletteGame = RouletteGame(initialRoulette)
        val numThreads = 30
        val threads: MutableList<Thread> = ArrayList()
        for (i in 0 until numThreads) {
            val thread = Thread { rouletteGame.play() }
            threads.add(thread)
            thread.start()
        }
        for (thread in threads) {
            thread.join()
        }
        Assertions.assertThat(100 - numThreads).isEqualTo(rouletteGame.currentTotalStock())
    }
}

 

 

๐Ÿ“Œ ๊ทธ ๋‹ค์Œ์€?

์ด์ œ ํ•ด์•ผ ํ•  ์ผ์€ ํฌ๊ฒŒ 2๊ฐ€์ง€๋กœ ์ถ”๋ ค๋ณผ ์ˆ˜ ์žˆ๋‹ค.

1. ์™ธ๋ถ€ ์ €์žฅ์†Œ๋ฅผ ์—ฐ๋™ํ•ด์„œ ๋ฃฐ๋ ›์„ ๊ด€๋ฆฌ

2. ์›น ํ†ต์‹ ์„ ์ด์šฉํ•ด ์‹ค์ œ ์‚ฌ์šฉ์ž๊ฐ€ ๋ฃฐ๋ › ๊ฒŒ์ž„์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“œ๋Š” ๊ฒƒ.

 

์–ธ์ œ ๋‹ค์Œ ์‹œ๋ฆฌ์ฆˆ๋ฅผ ์“ธ์ง€๋Š” ๋ชฐ๋ฃจ๊ฒ ์ง€๋งŒ 2ํŽธ์—์„  ์™ธ๋ถ€ ์ €์žฅ์†Œ๋ฅผ ์—ฐ๋™ํ•ด์„œ ๋ฃฐ๋ › ์ •๋ณด๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฒ•์„ ๋‹ค๋ค„๋ณด๊ฒ ๋‹ค.

 


 

 

github

https://github.com/hugehoo/spring-principle/tree/main/src/main/java/com/spring/principle/examples/roulette

 

 

ref.

https://docs.oracle.com/javase/9/docs/api/java/util/NavigableMap.html

https://tech.socarcorp.kr/dev/2021/10/19/sub-interfaces-navigablemap.html

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.random/-random/