确定布尔函数的外轮廓

Determine outer contour of boolean function

我想确定定义在两个变量上的布尔函数的轮廓,每个变量都在 0、1 范围内。我无权访问该函数的显式形式:它是一个黑框,每对变量 returns 一个布尔值,所以我不知道如何将它转换为连续函数并使用第一个下面回答。该区域是凸的,它可能到达参数范围的边缘。语言是 python。该函数的计算成本可能很高,因此我在下面使用的强力网格方法除了不准确之外还很慢。我该如何改进?

import numpy as np
import matplotlib.pyplot as plt

def f(x, y): return ((x-0.3)**2+(y-0.4)**2 <= 0.1) # Example of function; may extend outside region.  In reality, form is not known.

xs, ys = np.linspace(0, 1, 11), np.linspace(0, 1, 11) # Brute force grid
for x in xs:
    for y in ys:  plt.plot(x, y, 'ro' if f(x, y) else 'b+')

cntr = [] # List of unique, approximate contour points
for x in xs:
    col = [y for y in ys if f(x, y)]
    if (col != []) and (not ((x, min(col)) in cntr)): cntr.append((x, min(col))) 
    if (col != []) and (not ((x, max(col)) in cntr)): cntr.append((x, max(col))) 
for y in ys:
    row = [x for x in xs if f(x, y)]
    if (row != []) and (not ((min(row), y) in cntr)): cntr.append((min(row), y)) 
    if (row != []) and (not ((max(row), y) in cntr)): cntr.append((max(row), y)) 

plt.plot(np.transpose(cntr)[0], np.transpose(cntr)[1], 'kv') # Contour in black
plt.show()

编辑:级别对@Arne 回答的影响说明:

当然,这总是有点近似,因为您无法对每个点进行采样,但是有两个函数可以帮助简化代码并加快速度:np.meshgrid() and plt.contour()。后者允许指定要绘制的 levels,因此您可以通过将阈值保留在布尔函数之外来定义实值函数,并将阈值作为要绘制的级别传递给 plt.contour() :

import numpy as np
import matplotlib.pyplot as plt

def f_continuous(x, y): 
    return (x - 0.3)**2 + (y - 0.4)**2  # real-valued instead of Boolean;
    # define the 0.1 threshold in the contour plot function instead
    
xs, ys = np.linspace(0, 1, 11), np.linspace(0, 1, 11) # Brute force grid
X, Y = np.meshgrid(xs, ys)

plt.contour(X, Y, f_continuous(X, Y), levels=[0.1])
plt.show()

编辑:如果函数是黑盒,不能变成连续的怎么办?

您仍然可以使用 plt.contour(),方法是传递一个介于布尔值 0 和 1 之间的值作为轮廓级别。在这种情况下,插值结果并不平滑:

plt.contour(X, Y, f(X, Y), levels=[0.5])
plt.show()

作为您链接到笔记的答案,您可以通过捕获 plt.contour() 返回的 QuadContourSet 对象并访问其 allsegs 来获取轮廓线上计算点的坐标属性:

contour = plt.contour(X, Y, f(X, Y), levels=[0.5])
contour.allsegs
[[array([[0.  , 0.25],
         [0.05, 0.2 ],
         [0.1 , 0.15],
         ...
         [0.1 , 0.65],
         [0.05, 0.6 ],
         [0.  , 0.55]])]]