The general approach I was considering was an agent-based model where each agent's "values" are represented by a set of n real numbers in [0,1]: basically each dimension is like one of those survey questions that's a statement like "everyone should have access to clean drinking water," with answers ranging from "strongly disagree" to "strongly agree," and "neutral" in the middle (0.5).
And when two agents had a lot in common with one another values-wise (i.e., were nearby in n-dimensional space), then they would be more likely to move closer together. So starting with a randomly generated population, you'd see different subgroups start to coalesce into one or more distinct cultures with shared values (and eventually reducing to a single consensus culture if the simulation ran long enough).
Which as I poked around looked a lot like what was done in this 1997 paper (PDF) by Robert Axelrod (nullus), The Dissemination (nullus) of Culture. He puts his agents on a grid and only lets them potentially interact with grid neighbors. It's neat.
Here's a Python implementation, with visualization in PyGame (I inverted Axelrod's color coding so more similar sites are connected by darker lines, which strikes me as more intuitive):
import random import pygame from pygame.locals import * class Simulation: WIDTH = 640 HEIGHT = 480 LINE = 4 DRAW_FRAMES = 1000 def __init__(self, dimensions): self.dimensions = dimensions self.grid_size = min(self.WIDTH/(self.dimensions + 1), self.HEIGHT/(self.dimensions + 1)) self.bg_rect = pygame.Rect(max(0, (self.WIDTH - (self.dimensions + 1) * self.grid_size) / 2), max(0, (self.HEIGHT - (self.dimensions + 1) * self.grid_size) / 2), (self.dimensions + 1) * self.grid_size, (self.dimensions + 1) * self.grid_size) self.sites = [[[random.randint(1,10) for i in range(5)] for y in range(self.dimensions)] for x in range(self.dimensions)] def background(self, screen): screen.fill((255,255,255), self.bg_rect) def similarity(self, p1, p2): return sum(c1 == c2 for c1, c2 in zip(p1, p2))/float(len(p1)) def color(self, p1, p2): gray = 255 - int(self.similarity(p1, p2) * 255) return (gray, gray, gray) def lines(self, screen): for x in range(self.dimensions): for y in range(self.dimensions): start = (self.bg_rect.left + (x+1) * self.grid_size, self.bg_rect.top + (y+1) * self.grid_size) if x < self.dimensions - 1: end = (start + self.grid_size, start) color = self.color(self.sites[x][y], self.sites[x+1][y]) pygame.draw.line(screen, color, start, end, self.LINE) if y < self.dimensions - 1: end = (start, start + self.grid_size) color = self.color(self.sites[x][y], self.sites[x][y+1]) pygame.draw.line(screen, color, start, end, self.LINE) def random_site(self): return (random.randint(0, len(self.sites)-1), random.randint(0, len(self.sites)-1)) def random_neighbor(self, site): x,y = site neighbors =  if x > 0: neighbors.append((x-1,y)) if x < len(self.sites)-1: neighbors.append((x+1,y)) if y > 0: neighbors.append((x,y-1)) if y < len(self.sites)-1: neighbors.append((x,y+1)) return random.sample(neighbors, 1) def interact(self, active, neighb): different = [i for i in range(len(active)) if active[i] != neighb[i]] if len(different) > 0: i = random.sample(different, 1) active[i] = neighb[i] def try_event(self): active = self.random_site() neighb = self.random_neighbor(active) active_site = self.sites[active][active] neighb_site = self.sites[neighb][neighb] if random.random() < self.similarity(active_site, neighb_site): self.interact(active_site, neighb_site) def run(self): pygame.init() screen = pygame.display.set_mode((self.WIDTH,self.HEIGHT), HWSURFACE) pygame.display.set_caption('Sites') done = False n = 0 while not done: for event in pygame.event.get(): if event.type == QUIT: done = True elif event.type == KEYDOWN: if event.key == K_ESCAPE: done = True self.try_event() n += 1 if n > self.DRAW_FRAMES: self.background(screen) self.lines(screen) pygame.display.flip() n = 0 if __name__ == '__main__': Simulation((10,10)).run()
1 For extremely nerdy reasons.