데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      랭턴의 개미와 마르코프 체인으로 보는 고객 채널 점유율 시뮬레이션

      donuts 25.07.03
      1,063 7 2
      DEVOTEE 요약
      랭턴의 개미는 간단한 규칙으로 복잡한 패턴을 생성하며, 불규칙한 단계를 지나 결국 반복적인 고속도로 패턴으로 안정화됩니다. 마케팅 시뮬레이션에서 마르코프 체인을 활용하면 고객 채널 점유율의 변화와 정상 상태로의 수렴을 분석할 수 있으며, 단순한 조건 변화만으로는 제한적인 효과가 나타날 수 있습니다. 이를 통해 초기 규칙과 데이터를 기반으로 변화와 안정성을 분석하고, 웹/이메일과 같은 특정 채널 활성화에는 구조적 조정이 중요하다는 점이 강조됩니다.

      1. 랭턴의 개미란?

      랭턴의 개미(Langton's Ant) 는 매우 단순한 규칙을 따르는 2차원 격자 위의 개미입니다.

      해당 개미는 흰색 칸에서는 시계방향으로 90도 회전하고, 칸 색을 반전(흰색->검은색, 검은색->흰색)한 뒤 한 칸 전진하며,

      검은색 칸에서는 반시계방향으로 90도 회전하고, 칸 색을 반전하고 한 칸을 전진하는데요,

      이 단순한 규칙만으로도 격자 위에 신기한 패턴을 그리게 됩니다.


      아래는 초기 - 중기 - 후기로 나누어 랭턴의 개미의 행동 과정을 설명한 내용입니다.

      초기 : 예측 불가하며, 변화무쌍한 상태

      스크린샷 2025-06-28 오후 7.26.34.png

      처음에는 모든 격자가 흰색이고, 개미가 가운데에 위치합니다.

      개미는 규칙에 따라 움직이지만 초기에는 격자 위에 남기는 패턴이 매우 단순하거나 대칭적입니다.

      몇 백 번 움직인 후에는 격자 위에 무질서한 흑백 무늬가 형성되기 시작합니다.

      중기 : 불규칙한 구조 심화

      스크린샷 2025-06-28 오후 7.27.16.png

      수천 번의 이동 후, 개미는 격자 위를 마치 무작위처럼 움직이며 불규칙하고 복잡한 패턴을 만듭니다.

      이 단계에서는 격자 위에 남겨지는 흑백 무늬가 매우 혼란스럽고, 어떤 규칙성도 보이지 않습니다.

      후기 : 질서가 생기고, 반복되는 패턴으로 안정화

      스크린샷 2025-06-28 오후 7.27.47.png

      약 만 번의 이동 이후, 개미는 갑자기 반복적인 고속도로 패턴을 만들기 시작합니다.

      이 패턴은 104번 이동마다 반복되며, 개미는 이 패턴을 계속해서 따라가면서 격자 한쪽으로 빠져나갑니다.

      이처럼 단순한 규칙이 반복될 때 예측할 수 없는 복잡한 패턴이 나타나고, 결국에는 안정적인 반복 구조로 수렴하는 것이 랭턴의 개미의 특징입니다.

      랭턴의 개미와 마케팅 시뮬레이션

      • 마케팅 시뮬레이션도 랭턴의 개미와 유사하게, 단순한 규칙 (ex - 고객 이동, 채널 전환 등)이 반복되면 예측 불가능한 변화와 복잡한 결과가 나타날 수 있습니다.

      • 단기적으로는 변화가 불규칙하지만, 장기적으로는 랭턴의 개미처럼 안정적인 정상 상태로 수렴하는 경향이 있습니다.


      2. 고객 접점 점유율과 시뮬레이션

      1) 마르코프 체인을 활용한 고객 채널 점유율 예측
      • 새로운 사업이나 서비스를 시작할 때, 고객이 어떤 채널을 주로 이용하는지, 그리고 시간이 지남에 따라 그 점유율이 어떻게 변화하는지 예측하는 것은 매우 중요합니다.

      • 마르코프 체인을 활용하면 랭턴의 개미처럼 각 채널의 점유율이 '정상 상태'로 어떻게 수렴하는지 시뮬레이션할 수 있습니다.

      2) 기본 채널 전이 확률
      • 기본적으로 고객들이 아래와 같은 확률로 채널 간 전이를 한다고 가정합니다.

      • 상담센터 50%, 오프라인 매장 30%, 온라인 앱 15%, 웹/이메일 등 기타 5%

      • 그리고 기본적으로 랭턴의 개미처럼, 고객들은 현재 채널에 대해 20%정도의 추가적인 매력도를 느낀다고 가정합니다.

      states = ["상담센터", "오프라인 매장", "온라인 앱", "웹/이메일"]
      P = np.array([
          [0.70, 0.20, 0.08, 0.02],
          [0.30, 0.50, 0.15, 0.05],
          [0.40, 0.10, 0.45, 0.05],
          [0.60, 0.20, 0.15, 0.05]
      ])
      init = np.array([0.50, 0.30, 0.15, 0.05])
      3) 정상 상태 점유율

      각 채널 별 점유율은 아래와 같은 루트를 통해 수렴하게 됩니다.

      스크린샷 2025-06-28 오후 7.50.12.png

      스크린샷 2025-06-28 오후 7.49.56.png

      스크린샷 2025-06-28 오후 7.52.09.png

      초기 상태와는 달리, 50%였던 상담센터는 54% 가량으로, 30%의 오프라인 매장은 26%로, 15%였던 앱은 16%로, 5%였던 웹/이메일은 약 3% 가량으로 변경되게 됩니다.

      4) 현재 채널에 대해 더 큰 추가 가중치 적용

      만약 고객들이 경험한 채널에 대해 좀 더 애착을 가지고 자주 사용한다고 가정하고, 해당 채널에 1.4배의 가중치를 추가적으로 더 주게 되면 어떻게 바뀔까요?

      states = ["상담센터", "오프라인 매장", "앱", "웹/이메일"]
      P = np.array([
          [0.70, 0.20, 0.08, 0.02],
          [0.30, 0.50, 0.15, 0.05],
          [0.40, 0.10, 0.45, 0.05],
          [0.60, 0.20, 0.15, 0.05]
      ])
      init = np.array([0.50, 0.30, 0.15, 0.05])
      # 현재 경험한 채널에 40%의 가중치 추가
      def adjust_transition_matrix(P, alpha=1.2, beta=0.8):
          P_new = np.zeros_like(P)
          for i in range(P.shape[0]):
              for j in range(P.shape[1]):
                  if i == j:
                      P_new[i, j] = P[i, j] * alpha
                  else:
                      P_new[i, j] = P[i, j] * beta
              P_new[i, :] /= P_new[i, :].sum()
          return P_new
      
      P_adjusted = adjust_transition_matrix(P)

      스크린샷 2025-06-28 오후 8.55.58.png

      이 때는 초기에 큰 비중을 가지고 있던 상담센터의 비율이 매우 높아지고, 앱은 잠시 비중이 높아지다가 다시 수렴하며, 나머지 채널들은 각각 감소하는 방향으로 바뀌게 됩니다.

      5) 시뮬레이션 적용 - 온라인 채널의 활성화 (1단계)

      디지털 전환과 리소스 효율화로 인해, 온라인 채널을 활성화하려고 합니다.

      온라인 앱이나 웹에 추가적인 쿠폰을 주거나, 무료 배송 등의 프로모션을 진행해, 상담센터와 오프라인 매장 방문 비중을 줄이고 (-10%), 앱과 웹/이메일의 비중을 높이려고 합니다. (+20%)

      프로모션을 통해 고객 분들이 현재 경험한 채널에서 벗어나 좀 더 다양한 채널을 경험할 수 있게 유도하였습니다. (+40% -> -40%)

      # 채널 의존도 조정 (+40% -> -40%)
      def adjust_transition_matrix(P, alpha=0.8, beta=1.2):
          P_new = np.zeros_like(P)
          for i in range(P.shape[0]):
              for j in range(P.shape[1]):
                  if i == j:
                      P_new[i, j] = P[i, j] * alpha
                  else:
                      P_new[i, j] = P[i, j] * beta
              # 정규화
              P_new[i, :] /= P_new[i, :].sum()
          return P_new
      P_adjusted = adjust_transition_matrix(P)
      ...
      P_promo = P_adjusted.copy()
      # 앱, 웹/이메일로의 전이 확률 증가 (+20%)
      P_promo[:, 2] *= 1.2; P_promo[:, 3] *= 1.2
      # 오프라인, 상담센터로의 전이 확률 감소 (-10%)
      P_promo[:, 0] *= 0.9; P_promo[:, 1] *= 0.9
      # 정규화
      for i in range(P_promo.shape[0]):
          P_promo[i, :] /= P_promo[i, :].sum()

      스크린샷 2025-06-28 오후 8.57.55.png

      하지만 오프라인 매장의 경우에는 유의미한 수치로 줄어들었지만, 상담센터의 비중은 여전히 높고, 웹/이메일의 경우 거의 비중이 변하지 않았습니다.

      그렇다면 좀 더 강한 프로모션을 진행해본다면 어떨까요? 기존 채널에서 벗어나진 않고는 못 버틸 정도로 매력적인 프로모션을 제공하겠습니다.

      6) 시뮬레이션 적용 - 온라인 채널의 활성화 (2단계)

      더 강력한 프로모션을 진행해, 상담센터와 오프라인 매장 방문 비중을 줄이고 (-30%), 앱과 웹/이메일의 비중을 높이려고 합니다. (+50%)

      강력한 프로모션을 통해 고객 분들이 현재 경험한 채널에서 벗어나 좀 더 다양한 채널을 경험할 수 있게 유도하였습니다. (-40% -> -80%)

      # 채널 의존도 조정 (-40% -> -80%)
      def adjust_transition_matrix(P, alpha=0.6, beta=1.4):
          P_new = np.zeros_like(P)
          for i in range(P.shape[0]):
              for j in range(P.shape[1]):
                  if i == j:
                      P_new[i, j] = P[i, j] * alpha
                  else:
                      P_new[i, j] = P[i, j] * beta
              # 정규화
              P_new[i, :] /= P_new[i, :].sum()
          return P_new
      
      P_adjusted = adjust_transition_matrix(P)
      P_promo = P_adjusted.copy()
      # 온라인앱, 웹/기타로의 전이 확률 증가 (+50%)
      P_promo[:, 2] *= 1.5; P_promo[:, 3] *= 1.5
      # 오프라인, 상담센터로의 전이 확률 감소 (-30%)
      P_promo[:, 0] *= 0.7; P_promo[:, 1] *= 0.7
      # 정규화
      for i in range(P_promo.shape[0]):
          P_promo[i, :] /= P_promo[i, :].sum()

      스크린샷 2025-06-28 오후 8.59.34.png

      상담센터와 오프라인 매장 방문이 유의미하게 줄어들고, 앱 방문률은 큰 폭으로 늘어나 상담센터를 이은 2위로 올라갔습니다.

      하지만 웹/이메일의 경우 파격적인 프로모션에도 불구하고, 조금 늘어나긴 했지만 엇비슷한 값으로 수렴하게 되었습니다.


      실질적으로 웹/이메일의 점유율을 올리려면 전이행렬의 기본값 자체를 높이는 구조적 변화가 필요합니다.


      3. 결론

      랭턴의 개미와 마르코프 체인을 활용한 시뮬레이션을 통해, 단순한 규칙이 반복될 때 초기에는 예측할 수 없는 복잡한 변화가 나타나지만,

      시간이 지나면 결국 안정적인 상태로 수렴한다는 사실을 확인할 수 있었습니다.

      채널 점유율 역시 단기적으로는 다양한 변동 요인에 따라 불규칙하게 움직이지만, 장기적으로는 각 채널의 고유한 전이 확률에 따라 정상 상태로 수렴합니다.

      특히, 단순한 채널 이동 확률 상승이나 강화만으로는 웹/이메일의 점유율을 크게 높이기 어렵다는 점이 시뮬레이션 결과에서 드러났습니다.

      웹/이메일 채널의 점유율을 높이려면 기본값 자체를 구조적으로 변경하는 마케팅 전략이 필요하다는 사실을 알 수 있었습니다.

      이번 시뮬레이션은 단순한 초기 상태와 확률을 통한 간단한 시뮬레이션이지만, 고객의 채널 경험을 유도하거나 점유율을 예측하는 작업이 얼마나 복잡하고 어려운지,

      그리고 데이터 기반의 구조적 접근이 얼마나 중요한지 실감할 수 있었습니다.


      4. 추가 자료

      아래는 랭턴의 개미 시뮬레이션을 파이썬으로 구현한 예시입니다. 이 코드를 통해 랭턴의 개미를 각 단계별로 시뮬레이션 해보실 수 있습니다.

      관련 코드 : 
      import numpy as np
      import plotly.graph_objects as go
      from plotly.subplots import make_subplots
      
      class LangtonsAnt:
          def __init__(self, grid_size=100):
              self.N = grid_size
              self.grid = np.zeros((self.N, self.N), dtype=int) 
              self.x = self.y = self.N // 2                     
              self.dir = 0                                    
          
          def step(self):
              if self.grid[self.y, self.x] == 0: 
                  self.dir = (self.dir + 1) % 4
                  self.grid[self.y, self.x] = 1
              else:                             
                  self.dir = (self.dir - 1) % 4
                  self.grid[self.y, self.x] = 0
              
              if self.dir == 0: self.y = (self.y - 1) % self.N
              elif self.dir == 1: self.x = (self.x + 1) % self.N
              elif self.dir == 2: self.y = (self.y + 1) % self.N
              else: self.x = (self.x - 1) % self.N
          
          def run_and_capture(self, target_steps):
              captured = {}
              for step in range(1, max(target_steps) + 1):
                  self.step()
                  if step in target_steps:
                      captured[step] = (self.grid.copy(), self.get_position())
              return captured
          
          def get_position(self):
              return self.x, self.y, self.dir
      
      target_steps = [1, 100, 1000,       
                      3000, 5000, 10000, 
                      15000, 20000, 25000]  
      
      ant = LangtonsAnt(100)
      results = ant.run_and_capture(target_steps)
      
      fig = make_subplots(rows=3, cols=3,
                          subplot_titles=[f"Step {step}" for step in target_steps],
                          vertical_spacing=0.08, horizontal_spacing=0.05)
      
      for i, step in enumerate(target_steps):
          row, col = divmod(i, 3)
          grid, (x, y, dir) = results[step]
          
          fig.add_trace(
              go.Heatmap(
                  z=grid,
                  colorscale=[[0, '#FFFFFF'], [1, '#000000']],
                  showscale=False,
                  hoverinfo='none'
              ),
              row=row+1, col=col+1
          )
          
          fig.add_trace(
              go.Scatter(
                  x=[x], y=[y],
                  mode='markers',
                  marker=dict(
                      symbol='triangle-up' if dir == 0 else 
                             'triangle-right' if dir == 1 else 
                             'triangle-down' if dir == 2 else 'triangle-left',
                      size=14,
                      color='#FF0000',
                      line=dict(width=2, color='#000000')
                  ),
                  showlegend=False
              ),
              row=row+1, col=col+1
          )
      
      fig.update_layout(
          title="랭턴의 개미 진화 단계 (초기 → 불규칙 → 고속도로)",
          width=1200,
          height=1600,
          margin=dict(t=100),
          plot_bgcolor='#F0F0F0'
      )
      fig.update_xaxes(showticklabels=False, zeroline=False)
      fig.update_yaxes(showticklabels=False, zeroline=False, scaleanchor="x")
      fig.show()

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      donuts 님의 최신 블로그

      더보기
      동영상 기고하기