use std::io::{stdin, Read};
use std::str::FromStr;
#[allow(unused_imports)]
use std::{cmp, mem, process};
#[allow(unused_imports)]
use std::collections::{HashSet, BTreeMap, BTreeSet};
fn calc(x: &Vec<usize>) -> usize {
let mut y = x.clone();
while y.len() > 1 {
let mut nxt = vec![];
for i in 0..y.len() - 2 {
let mut temp = vec![y[i], y[i + 1], y[i + 2]];
temp.sort();
nxt.push(temp[1]);
}
y = nxt;
}
y[0]
}
fn solve(n: usize, x: usize) {
// 3 ならば 1,7 はでない
// 4 ならば 1,2 6,7 はでない
// 5 ならば 1,2,3 7,8,9 はでない
if n == 2 {
if x == 2 {
println!("Yes");
println!("1 2 3");
} else {
println!("No");
}
return;
}
if 1 != x && 2 * n - 1 != x {
// ok
//println!("{} {} ", n, x);
let mut ans = vec![0; 2 * n - 1];
let mut memo = vec![false; 2 * n + 10];
if x != 2 {
ans[n - 1] = x;
ans[n - 2] = x - 1;
ans[n] = x + 1;
ans[n + 1] = x - 2;
for i in x - 2..x + 2 {
memo[i] = true;
}
} else {
ans[n - 1] = x;
ans[n - 2] = x + 1;
ans[n] = x - 1;
ans[n + 1] = x + 2;
for i in x - 1..x + 3 {
memo[i] = true;
}
}
let mut cnt = 1;
for i in 0..2 * n - 1 {
if ans[i] != 0 {
continue;
} else {
while memo[cnt] {
cnt += 1;
}
ans[i] = cnt;
memo[cnt] = true;
}
}
// if calc(&ans) != x {
// assert!(false);
// }
println!("Yes");
for i in &ans {
println!("{}", *i);
}
} else {
println!("No");
}
}
fn main() {
// for i in 2..20 {
// for j in 1..2 * i {
// solve(i, j);
// }
// }
let n = read::<usize>();
let x = read::<usize>();
solve(n, x);
}
#[allow(dead_code)]
fn read_vec<T: FromStr>(n: usize) -> Vec<T> {
let mut v = vec![];
for _ in 0..n {
let a = read::<T>();
v.push(a);
}
v
}
#[allow(dead_code)]
fn read_str() -> Vec<char> {
let s: String = read();
s.chars().collect::<Vec<char>>()
}
#[allow(dead_code)]
fn read<T: FromStr>() -> T {
let stdin = stdin();
let stdin = stdin.lock();
let s = stdin
.bytes()
.map(|c| c.unwrap() as char)
.skip_while(|c| c.is_whitespace())
.take_while(|c| !c.is_whitespace())
.collect::<String>();
s.parse::<T>().unwrap_or_else(
|_| panic!("Faild to parse {}", s),
)
}